javscript的代碼只能運行在單線程中,也就是一個nodejs進程只能運行在一個cpu上。若是須要充分利用多核cpu的併發優點,可使用cluster模塊。cluster可以建立多個子進程,每一個進程都運行同一份代碼,而且監聽的是同一個端口。html
簡單利用Cluster fork cpu個數子進程的代碼以下:node
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { // 若是是Master則進行fork操做,啓動其餘進程 for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', function(worker, code, signal) { console.log('worker ' + worker.process.pid + ' died'); }); } else { // 不然啓動http服務監聽 http.createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); }
爲何cluster fork多份源碼跑在多個子進程上沒有報端口被佔用?
cluster模塊會hack掉worker中的監聽,端口僅由master的TCP服務監聽了一次segmentfault
在eggjs中,除了有worker還有Agent,實際上也是一個worker,爲了區別把他們稱爲agent worker和app worker。
Agent worker的做用是用來處理一些後臺運行邏輯,好比說打印日誌,不須要在4個app worker上都去執行,不對外提供服務,只處理公共事務,因此穩定性相對來講是很高的。併發
+--------+ +-------+ | Master |<-------->| Agent | +--------+ +-------+ ^ ^ ^ / | \ / | \ / | \ v v v +----------+ +----------+ +----------+ | Worker 1 | | Worker 2 | | Worker 3 | +----------+ +----------+ +----------+
Master-Agent-Worker模型下,master承擔了相似於pm2的進程管理的職責,可以完成worker的初始化/重啓等工做。
app
異常能夠簡單分爲兩類,第一類是能夠監聽process.on('uncaughtException', handler)
捕獲的異常,經過監聽事件可使得進程不會異常推出還有機會能夠繼續執行。第二類是被系統殺死直接推出的異常。
eggjs使用了graceful和egg-cluster讓異常發生時master可以馬上fork出一個新的worker保持鏈接的worker數。負載均衡
進程啓動順序框架
+---------+ +---------+ +---------+ | Master | | Agent | | Worker | +---------+ +----+----+ +----+----+ | fork agent | | +-------------------->| | | agent ready | | |<--------------------+ | | | fork worker | +----------------------------------------->| | worker ready | | |<-----------------------------------------+ | Egg ready | | +-------------------->| | | Egg ready | | +----------------------------------------->|
Master 啓動後先 fork Agent 進程,同時監聽'agent-exit, agent-start'事件,agent 啓動成功後發送agent-start
事件(IPC進程間通訊)通知masterui
agent-start
通知fork多個App Worker,這裏的fork用的是cfork包,負責 worker 的啓動,狀態監聽以及 refork 操做,保證worker的數量app-start
事件通知到master在nodejs中實現進程通訊能夠經過監聽messgae事件實現this
'use strict'; const cluster = require('cluster'); if (cluster.isMaster) { const worker = cluster.fork(); worker.send('hi there'); worker.on('message', msg => { console.log(`msg: ${msg} from worker#${worker.id}`); }); } else if (cluster.isWorker) { process.on('message', (msg) => { process.send(msg); }); }
在eggjs Agent機制中,agent也是也給worker,因此IPC通道存在與master和agent/app worker之間,而agent和app worker之間的通訊須要經過master轉發。
Eggjs包裝了Message類,用from
to
標記淶源和去向。spa
this.messenger.send({ action: 'agent-exit', data: { code, signal }, to: 'master', from: 'agent', });
https://juejin.im/entry/59bcce1b5188257e82676b53
https://zhuanlan.zhihu.com/p/49276061
http://www.javashuo.com/article/p-hljxobfr-db.html
https://eggjs.org/zh-cn/core/cluster-and-ipc.html