node.js 下 cluster 模块充分利用cpu资源实现群集功能
为什么会出现cluster ?
大概在 node.js V0.8 之前的版本,node.js 本身不提供多核多进程处理的解决方案,当然你可以使用第三方的开源包来实现.
直到V0.8之后的版本,node.js 内置了 cluster 功能,node.js 终于可以不依赖第三方cluster包来利用服务器多核,多线程资源.
node.js 内置模块 cluster 的出现,帮助我们轻松利用多核,多进程开发的难度及负载均衡群集.
cluster 是如何工作的 ?
工作进程是通过使用 child_process.fork 方法派送的,他们可以通过IPC (进程间通讯实现父进程和子进程相互传递句柄及通讯)
看下面的一个示例:
var cluster = require('cluster'); var http=require('http'); var cpu_num = require('os').cpus().length; if (cluster.isMaster) { console.log("==启动主进程=="); for (var i = 0; i < cpu_num; i++) { cluster.fork(); //根据cpu的数量主进程 fork 了相同数量的子进程出来 } cluster.on('listening',function(worker,address){ console.log('listening: worker ' + worker.process.pid); }); cluster.on('exit', function(worker, code, signal) { console.log('exit worker ' + worker.process.pid + ' died'); }); } else { http.createServer(function(req, res) { res.writeHead(200); res.end("<a href='http://yijiebuyi.com'>一介布衣</a>\n"); }).listen(7000); }
29 Jul 17:44:02 - [nodemon] starting `node cluster.js` ==启动主进程== listening: worker 39114 listening: worker 39116 listening: worker 39113 listening: worker 39115
看到上面的运行结果,主进程启动了4个子进程
其实我电脑是双核的,见下图
因为双通道,它认为是4核
cluster 默认有2个参数可以区分主进程和子进程.
cluster.isMaster 判断主进程: 返回 true or false
cluster.isWorker 判断子进程: 返回 true or false
if (cluster.isMaster) { 这个逻辑判断分支里,主进程会根据cpu个数来 fork 相同数目的子进程 }else{ 这里的逻辑分支是,如果是子进程,就监听 7000 端口. }
这样看来,我们4个子进程同时监听相同端口 7000 ,那么主进程干什么去了?
其实这时的主进程起到了协调,调度的作用,他把客户端过来的请求分发给子进程.
cluster 是如何调度协调子进程的?
cluster 内部有2中方法来调度
(1)循环调度
(2)系统调度
除windows 系统外,默认都是采用第一种 循环调度 的方法.
就是一共4个子进程,当主进程接到请求后,挨个从 子进程1到4派发任务,这样一直循环下去.
第二种系统调度,可能更加智能一点点,就是主进程拿到请求后优先派发给空闲的进程去处理.
每个子进程都是独立的进程,它们会根据您的程序的需要被终止或重新派生,
不会影响到其它工作进程。只要还有工作进程存在,服务器就会继续接受连接。
但是,Node 不会自动为您管理工作进程的数量.
cluster 主要的api有哪些?
fork
用来派生一个子进程
cluster.fork();
当主进程创建派生子进程时,会触发此事件.
2. online
当fork 一个新的进程后,子进程会通知主进程,当主进程收到子进程的消息后,这时会触发 online 事件
online 和 fork 有什么区别?
针对主进程来说, fork 是一个主动的工程,当我派生新子进程后,及触发此事件.
online 对于主进程来说,是比较被动的,当子进程被创建后和主进程通讯成功,这时主进程才会触发 online 事件.
3.listening
工作子进程监听的地址和端口.
cluster.on('listening', function(worker, address) { console.log("工作进程连接到 " + address.address + ":" + address.port); });
4. exit
工作子进程退出时,cluster 模块会分发此事件.
cluster.on('exit', function(worker, code, signal) { var exitCode = worker.process.exitCode; console.log('工作进程 ' + worker.process.pid + ' 被结束('+exitCode+')。正在重启...'); cluster.fork(); });
上面的示例代码不仅仅是监听cluster 的 exit 事件,更重要的功能是重启子进程
如何重启, cluster.fork() 即可把此子进程重启.
这种模式非常像 forever 的工作模式,一个守护进程来监视所有的子进程,当任何一个'死'掉,马上让他原地满血满状态复活.