淺談nodejs中創(chuàng)建cluster
cluster集群
我們知道,nodejs的event loop或者說事件響應(yīng)處理器是單線程的,但是現(xiàn)在的CPU基本上都是多核的,為了充分利用現(xiàn)代CPU多核的特性,我們可以創(chuàng)建cluster,從而使多個(gè)子進(jìn)程來共享同一個(gè)服務(wù)器端口。
也就是說,通過cluster,我們可以使用多個(gè)子進(jìn)程來服務(wù)處理同一個(gè)端口的請(qǐng)求。
先看一個(gè)簡(jiǎn)單的http server中使用cluster的例子:
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`主進(jìn)程 ${process.pid} 正在運(yùn)行`); // 衍生工作進(jìn)程。 for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`工作進(jìn)程 ${worker.process.pid} 已退出`); }); } else { // 工作進(jìn)程可以共享任何 TCP 連接。 // 在本例子中,共享的是 HTTP 服務(wù)器。 http.createServer((req, res) => { res.writeHead(200); res.end('你好世界\n'); }).listen(8000); console.log(`工作進(jìn)程 ${process.pid} 已啟動(dòng)`); }
cluster詳解
cluster模塊源自于lib/cluster.js,我們可以通過cluster.fork()來創(chuàng)建子工作進(jìn)程,用來處理主進(jìn)程的請(qǐng)求。
cluster中的event
cluster繼承自events.EventEmitter,所以cluster可以發(fā)送和接收event。
cluster支持7中event,分別是disconnect,exit,fork,listening,message,online和setup。
在講解disconnect之前,我們先介紹一個(gè)概念叫做IPC,IPC的全稱是Inter-Process Communication,也就是進(jìn)程間通信。
IPC主要用來進(jìn)行主進(jìn)程和子進(jìn)程之間的通信。一個(gè)工作進(jìn)程在創(chuàng)建后會(huì)自動(dòng)連接到它的主進(jìn)程。 當(dāng) 'disconnect' 事件被觸發(fā)時(shí)才會(huì)斷開連接。
觸發(fā)disconnect事情的原因有很多,可以是主動(dòng)調(diào)用worker.disconnect(),也可以是工作進(jìn)程退出或者被kill掉。
cluster.on('disconnect', (worker) => { console.log(`工作進(jìn)程 #${worker.id} 已斷開連接`); });
exit事件會(huì)在任何一個(gè)工作進(jìn)程關(guān)閉的時(shí)候觸發(fā)。一般用來監(jiān)測(cè)cluster中某一個(gè)進(jìn)程是否異常退出,如果退出的話使用cluster.fork創(chuàng)建新的進(jìn)程,以保證有足夠多的進(jìn)程來處理請(qǐng)求。
cluster.on('exit', (worker, code, signal) => { console.log('工作進(jìn)程 %d 關(guān)閉 (%s). 重啟中...', worker.process.pid, signal || code); cluster.fork(); });
fork事件會(huì)在調(diào)用cluster.fork方法的時(shí)候被觸發(fā)。
const timeouts = []; function errorMsg() { console.error('連接出錯(cuò)'); } cluster.on('fork', (worker) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
主進(jìn)程和工作進(jìn)程的listening事件都會(huì)在工作進(jìn)程調(diào)用listen方法的時(shí)候觸發(fā)。
cluster.on('listening', (worker, address) => { console.log( `工作進(jìn)程已連接到 ${address.address}:${address.port}`); });
其中worker代表的是工作線程,而address中包含三個(gè)屬性:address、 port 和 addressType。 其中addressType有四個(gè)可選值:
- 4 (TCPv4)
- 6 (TCPv6)
- -1 (Unix 域 socket)
- 'udp4' or 'udp6' (UDP v4 或 v6)
message事件會(huì)在主進(jìn)程收到子進(jìn)程發(fā)送的消息時(shí)候觸發(fā)。
當(dāng)主進(jìn)程生成工作進(jìn)程時(shí)會(huì)觸發(fā)fork,當(dāng)工作進(jìn)程運(yùn)行時(shí)會(huì)觸發(fā)online。
setupMaster方法被調(diào)用的時(shí)候,會(huì)觸發(fā)setup事件。
cluster中的方法
cluster中三個(gè)方法,分別是disconnect,fork和setupMaster。
cluster.disconnect([callback])
調(diào)用cluster的disconnect方法,實(shí)際上會(huì)在cluster中的每個(gè)worker中調(diào)用disconnect方法。從而斷開worker和主進(jìn)程的連接。
當(dāng)所有的worker都斷開連接之后,會(huì)執(zhí)行callback。
cluster.fork([env])
fork方法,會(huì)從主進(jìn)程中創(chuàng)建新的子進(jìn)程。其中env是要添加到進(jìn)程環(huán)境變量的鍵值對(duì)。
fork將會(huì)返回一個(gè)cluster.Worker對(duì)象,代表工作進(jìn)程。
最后一個(gè)方法是setupMaster:
cluster.setupMaster([settings])
默認(rèn)情況下,cluster通過fork方法來創(chuàng)建子進(jìn)程,但是我們可以通過setupMaster來改變這個(gè)行為。通過設(shè)置settings變量,我們可以改變后面fork子進(jìn)程的行為。
我們看一個(gè)setupMaster的例子:
const cluster = require('cluster'); cluster.setupMaster({ exec: 'worker.js', args: ['--use', 'https'], silent: true }); cluster.fork(); // https 工作進(jìn)程 cluster.setupMaster({ exec: 'worker.js', args: ['--use', 'http'] }); cluster.fork(); // http 工作進(jìn)程
cluster中的屬性
通過cluster對(duì)象,我們可以通過isMaster和isWorker來判斷進(jìn)程是否主進(jìn)程。
可以通過worker來獲取當(dāng)前工作進(jìn)程對(duì)象的引用:
const cluster = require('cluster'); if (cluster.isMaster) { console.log('這是主進(jìn)程'); cluster.fork(); cluster.fork(); } else if (cluster.isWorker) { console.log(`這是工作進(jìn)程 #${cluster.worker.id}`); }
可以通過workers來遍歷活躍的工作進(jìn)程對(duì)象:
// 遍歷所有工作進(jìn)程。 function eachWorker(callback) { for (const id in cluster.workers) { callback(cluster.workers[id]); } } eachWorker((worker) => { worker.send('通知所有工作進(jìn)程'); });
每個(gè)worker都有一個(gè)id編號(hào),用來定位該worker。
cluster中的worker
worker類中包含了關(guān)于工作進(jìn)程的所有的公共的信息和方法。cluster.fork出來的就是worker對(duì)象。
worker的事件和cluster的很類似,支持6個(gè)事件:disconnect,error,exit,listening,message和online。
worker中包含3個(gè)屬性,分別是:id,process和exitedAfterDisconnect。
其中id是worker的唯一標(biāo)記。
worker中的process,實(shí)際上是ChildProcess對(duì)象,是通過child_process.fork()來創(chuàng)建出來的。
因?yàn)樵趙orker中,process屬于全局變量,所以我們可以直接在worker中使用process來進(jìn)行發(fā)送消息。
exitedAfterDisconnect表示如果工作進(jìn)程由于 .kill() 或 .disconnect() 而退出的話,值就是true。如果是以其他方式退出的話,返回值就是false。如果工作進(jìn)程尚未退出,則為 undefined。
我們可以通過worker.exitedAfterDisconnect 來區(qū)分是主動(dòng)退出還是被動(dòng)退出,主進(jìn)程可以根據(jù)這個(gè)值決定是否重新生成工作進(jìn)程。
cluster.on('exit', (worker, code, signal) => { if (worker.exitedAfterDisconnect === true) { console.log('這是自發(fā)退出,無需擔(dān)心'); } }); // 殺死工作進(jìn)程。 worker.kill();
worker還支持6個(gè)方法,分別是:send,kill,destroy,disconnect,isConnected,isDead。
這里我們主要講解一下send方法來發(fā)送消息:
worker.send(message[, sendHandle[, options]][, callback])
可以看到send方法和child_process中的send方法參數(shù)其實(shí)是很類似的。而本質(zhì)上,worker.send在主進(jìn)程中,這會(huì)發(fā)送消息給特定的工作進(jìn)程。 相當(dāng)于 ChildProcess.send()。在工作進(jìn)程中,這會(huì)發(fā)送消息給主進(jìn)程。 相當(dāng)于 process.send()。
if (cluster.isMaster) { const worker = cluster.fork(); worker.send('你好'); } else if (cluster.isWorker) { process.on('message', (msg) => { process.send(msg); }); }
在上面的例子中,如果是在主進(jìn)程中,那么可以使用worker.send來發(fā)送消息。而在子進(jìn)程中,則可以使用worker中的全局變量process來發(fā)送消息。
總結(jié)
使用cluster可以充分使用多核CPU的優(yōu)勢(shì),希望大家在實(shí)際的項(xiàng)目中應(yīng)用起來。
以上就是淺談nodejs中創(chuàng)建cluster的詳細(xì)內(nèi)容,更多關(guān)于nodejs中創(chuàng)建cluster的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Node.js使用Koa搭建 基礎(chǔ)項(xiàng)目
時(shí)下前端工程師有很多人比較關(guān)注NodeJs以及express 框架或者Koa 框架之類的新技術(shù)。難得我最近閑時(shí)較多,利用一下舊歷新年尚未正式到來的這片閑暇,也來涉足其中,一窺其中奧妙。2018-01-01詳解一個(gè)基于套接字實(shí)現(xiàn)長(zhǎng)連接的express
這篇文章主要介紹了詳解一個(gè)基于套接字實(shí)現(xiàn)長(zhǎng)連接的express,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03npm?install的--save和--save-dev使用說明(推薦)
這篇文章主要介紹了npm?install的--save和--save-dev使用說明,文中給大家提到了各個(gè)命令的區(qū)別及各種安裝參數(shù)的區(qū)別,需要的朋友可以參考下2022-08-08通過實(shí)例了解Nodejs模塊系統(tǒng)及require機(jī)制
這篇文章主要介紹了通過實(shí)例了解Nodejs模塊系統(tǒng)及require機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07nodejs實(shí)現(xiàn)登陸驗(yàn)證功能
這篇文章主要為大家詳細(xì)介紹了nodejs實(shí)現(xiàn)登陸驗(yàn)證功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04nodejs npm install全局安裝和本地安裝的區(qū)別
這篇文章主要介紹了nodejs npm install 全局安裝和非全局安裝的區(qū)別,即帶參數(shù)-g和不帶參數(shù)-g安裝的區(qū)別,需要的朋友可以參考下2014-06-06