JavaScript 是单线程的,只能运行在一个CPU上,这样不能充分发挥计算机的性能。为了更好地利用多核环境,Node.js 提供了 Cluster 模块,可以方便的创建多个子进程,提高项目运行效率。
Cluster 模块
Cluster 模块将进程分为 Master 进程和 Worker 进程:
- 负责启动其他进程的叫做 Master 进程,只负责启动其他进程。
- 其他被启动的叫 Worker 进程,接收请求,对外提供服务。
- Worker 进程的数量一般根据服务器的 CPU 核数来定,这样就可以完美利用多核资源。
范例代码:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
// 判断是不是 Master 进程
if (cluster.isMaster) {
// 创建子进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Worker 进程能够共用TCP连接
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}
Cluster 原理
-
Master、Worker进程通信
master 进程通过
cluster.fork()来创建 worker进程。cluster.fork()内部 是通过child_process.fork()来创建子进程。所以 master进程、worker进程是父、子进程的关系。Master 进程、Woker进程之间通过IPC通道进行通信。 -
实现端口共享
net模块中,对
listen()方法进行了特殊处理。如果当前进程是 Master 进程则不做处理,如果是 Worker 进程则创建server实例。然后通过IPC通道,向 Master 进程发送消息,**让 Master 进程也创建 server 实例,并在该端口上监听请求。**当请求进来时,master进程将请求转发给worker进程的server实例。// lib/net.js 源代码 function listen(self, address, port, addressType, backlog, fd, exclusive) { exclusive = !!exclusive; // 引入 cluster 模块 if (!cluster) cluster = require('cluster'); // 判断当前进程是不是 Master 进程 if (cluster.isMaster || exclusive) { self._listen2(address, port, addressType, backlog, fd); return; } // _getServer 向 Master 进程注册该 Worker,若master进程是第一次接收到监听此端口/描述符下的Worker,则起一个内部TCP服务器,来承担监听该端口/描述符的职责,随后在master中记录下该worker。 // Hack掉worker进程中的net.Server实例的listen方法,使其不再监听接口。 cluster._getServer(self, { address: address, port: port, addressType: addressType, fd: fd, flags: 0 }, cb); function cb(err, handle) { // ... self._handle = handle; self._listen2(address, port, addressType, backlog, fd); } } -
分发多个请求到worker
每当worker进程创建server实例来监听请求,都会通过IPC通道,在master上进行注册。当客户端请求到达,master会负责将请求转发给对应的worker。
具体转发给哪个worker?这是由转发策略决定的。可以通过环境变量
NODE_CLUSTER_SCHED_POLICY设置,也可以在cluster.setupMaster(options)时传入。默认的转发策略是轮询(SCHED_RR):当有客户请求到达,master会轮询一遍worker列表,找到第一个空闲的worker,然后将该请求转发给该worker。
-
发送内部消息
当发送的消息包含
cmd字段,且改字段以NODE_作为前缀,则该消息会被视为内部保留的消息,不会通过message事件抛出,但可以通过监听’internalMessage’捕获。// woker进程 const message = { cmd: 'NODE_CLUSTER', act: 'queryServer' }; process.send(message); // master进程接收方式 worker.process.on('internalMessage', fn);
Eggjs 多进程模型
错误处理
在多进程的情况下,一个重要的要求就是保证程序出错的时候还能够正常运行,这就叫健壮性(又叫鲁棒性)。Eggjs 框架在这方面也提供了相关的保证。
Node.js 进程退出的两大情况
- 未捕获异常 Node.js 提供了
process.on('uncaughtException', handler)接口来捕获未被处理的异常。Eggjs 则采用关闭异常 Worker 进程所有的 TCP Server, Master 进程立刻 fork 一个新的 Worker 进程来保证服务正常进行。异常 Worker 在处理完请求后退出

本文详细介绍了Node.js的Cluster模块,用于在多核环境下提升项目性能。Eggjs在此基础上构建了多进程模型,包括错误处理、Agent机制以及进程间通讯。文中特别强调了Agent的作用,它是处理公共事务的独立进程,增强了系统的健壮性。此外,还讨论了负载均衡的Sticky Mode,确保请求被定向到相同的Worker,解决了Session问题。最后,探讨了Eggjs如何通过建立Agent和Worker的直接连接,优化了多进程研发模式。
最低0.47元/天 解锁文章
3万+

被折叠的 条评论
为什么被折叠?



