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