在nodejs中创建cluster
文章目錄
- 簡介
- cluster集群
- cluster詳解
- cluster中的event
- cluster中的方法
- cluster中的屬性
- cluster中的worker
- 總結
簡介
在前面的文章中,我們講到了可以通過worker_threads來創建新的線程,可以使用child_process來創建新的子進程。本文將會介紹如何創建nodejs的集群cluster。
cluster集群
我們知道,nodejs的event loop或者說事件響應處理器是單線程的,但是現在的CPU基本上都是多核的,為了充分利用現代CPU多核的特性,我們可以創建cluster,從而使多個子進程來共享同一個服務器端口。
也就是說,通過cluster,我們可以使用多個子進程來服務處理同一個端口的請求。
先看一個簡單的http server中使用cluster的例子:
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length;if (cluster.isMaster) {console.log(`主進程 ${process.pid} 正在運行`);// 衍生工作進程。for (let i = 0; i < numCPUs; i++) {cluster.fork();}cluster.on('exit', (worker, code, signal) => {console.log(`工作進程 ${worker.process.pid} 已退出`);}); } else {// 工作進程可以共享任何 TCP 連接。// 在本例子中,共享的是 HTTP 服務器。http.createServer((req, res) => {res.writeHead(200);res.end('你好世界\n');}).listen(8000);console.log(`工作進程 ${process.pid} 已啟動`); }cluster詳解
cluster模塊源自于lib/cluster.js,我們可以通過cluster.fork()來創建子工作進程,用來處理主進程的請求。
cluster中的event
cluster繼承自events.EventEmitter,所以cluster可以發送和接收event。
cluster支持7中event,分別是disconnect,exit,fork,listening,message,online和setup。
在講解disconnect之前,我們先介紹一個概念叫做IPC,IPC的全稱是Inter-Process Communication,也就是進程間通信。
IPC主要用來進行主進程和子進程之間的通信。一個工作進程在創建后會自動連接到它的主進程。 當 ‘disconnect’ 事件被觸發時才會斷開連接。
觸發disconnect事情的原因有很多,可以是主動調用worker.disconnect(),也可以是工作進程退出或者被kill掉。
cluster.on('disconnect', (worker) => {console.log(`工作進程 #${worker.id} 已斷開連接`); });exit事件會在任何一個工作進程關閉的時候觸發。一般用來監測cluster中某一個進程是否異常退出,如果退出的話使用cluster.fork創建新的進程,以保證有足夠多的進程來處理請求。
cluster.on('exit', (worker, code, signal) => {console.log('工作進程 %d 關閉 (%s). 重啟中...',worker.process.pid, signal || code);cluster.fork(); });fork事件會在調用cluster.fork方法的時候被觸發。
const timeouts = []; function errorMsg() {console.error('連接出錯'); }cluster.on('fork', (worker) => {timeouts[worker.id] = setTimeout(errorMsg, 2000); });主進程和工作進程的listening事件都會在工作進程調用listen方法的時候觸發。
cluster.on('listening', (worker, address) => {console.log(`工作進程已連接到 ${address.address}:${address.port}`); });其中worker代表的是工作線程,而address中包含三個屬性:address、 port 和 addressType。 其中addressType有四個可選值:
- 4 (TCPv4)
- 6 (TCPv6)
- -1 (Unix 域 socket)
- ‘udp4’ or ‘udp6’ (UDP v4 或 v6)
message事件會在主進程收到子進程發送的消息時候觸發。
當主進程生成工作進程時會觸發fork,當工作進程運行時會觸發online。
setupMaster方法被調用的時候,會觸發setup事件。
cluster中的方法
cluster中三個方法,分別是disconnect,fork和setupMaster。
cluster.disconnect([callback])調用cluster的disconnect方法,實際上會在cluster中的每個worker中調用disconnect方法。從而斷開worker和主進程的連接。
當所有的worker都斷開連接之后,會執行callback。
cluster.fork([env])fork方法,會從主進程中創建新的子進程。其中env是要添加到進程環境變量的鍵值對。
fork將會返回一個cluster.Worker對象,代表工作進程。
最后一個方法是setupMaster:
cluster.setupMaster([settings])默認情況下,cluster通過fork方法來創建子進程,但是我們可以通過setupMaster來改變這個行為。通過設置settings變量,我們可以改變后面fork子進程的行為。
我們看一個setupMaster的例子:
const cluster = require('cluster'); cluster.setupMaster({exec: 'worker.js',args: ['--use', 'https'],silent: true }); cluster.fork(); // https 工作進程 cluster.setupMaster({exec: 'worker.js',args: ['--use', 'http'] }); cluster.fork(); // http 工作進程cluster中的屬性
通過cluster對象,我們可以通過isMaster和isWorker來判斷進程是否主進程。
可以通過worker來獲取當前工作進程對象的引用:
const cluster = require('cluster');if (cluster.isMaster) {console.log('這是主進程');cluster.fork();cluster.fork(); } else if (cluster.isWorker) {console.log(`這是工作進程 #${cluster.worker.id}`); }可以通過workers來遍歷活躍的工作進程對象:
// 遍歷所有工作進程。 function eachWorker(callback) {for (const id in cluster.workers) {callback(cluster.workers[id]);} } eachWorker((worker) => {worker.send('通知所有工作進程'); });每個worker都有一個id編號,用來定位該worker。
cluster中的worker
worker類中包含了關于工作進程的所有的公共的信息和方法。cluster.fork出來的就是worker對象。
worker的事件和cluster的很類似,支持6個事件:disconnect,error,exit,listening,message和online。
worker中包含3個屬性,分別是:id,process和exitedAfterDisconnect。
其中id是worker的唯一標記。
worker中的process,實際上是ChildProcess對象,是通過child_process.fork()來創建出來的。
因為在worker中,process屬于全局變量,所以我們可以直接在worker中使用process來進行發送消息。
exitedAfterDisconnect表示如果工作進程由于 .kill() 或 .disconnect() 而退出的話,值就是true。如果是以其他方式退出的話,返回值就是false。如果工作進程尚未退出,則為 undefined。
我們可以通過worker.exitedAfterDisconnect 來區分是主動退出還是被動退出,主進程可以根據這個值決定是否重新生成工作進程。
cluster.on('exit', (worker, code, signal) => {if (worker.exitedAfterDisconnect === true) {console.log('這是自發退出,無需擔心');} });// 殺死工作進程。 worker.kill();worker還支持6個方法,分別是:send,kill,destroy,disconnect,isConnected,isDead。
這里我們主要講解一下send方法來發送消息:
worker.send(message[, sendHandle[, options]][, callback])可以看到send方法和child_process中的send方法參數其實是很類似的。而本質上,worker.send在主進程中,這會發送消息給特定的工作進程。 相當于 ChildProcess.send()。在工作進程中,這會發送消息給主進程。 相當于 process.send()。
if (cluster.isMaster) {const worker = cluster.fork();worker.send('你好');} else if (cluster.isWorker) {process.on('message', (msg) => {process.send(msg);}); }在上面的例子中,如果是在主進程中,那么可以使用worker.send來發送消息。而在子進程中,則可以使用worker中的全局變量process來發送消息。
總結
使用cluster可以充分使用多核CPU的優勢,希望大家在實際的項目中應用起來。
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/nodejs-cluster/
本文來源:flydean的博客
歡迎關注我的公眾號:「程序那些事」最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
總結
以上是生活随笔為你收集整理的在nodejs中创建cluster的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用nodejs构建Docker ima
- 下一篇: 不要在nodejs中阻塞event lo