简介
在后面的文章中,咱们讲到了能够通过worker_threads来创立新的线程,能够应用child_process来创立新的子过程。本文将会介绍如何创立nodejs的集群cluster。
cluster集群
咱们晓得,nodejs的event loop或者说事件响应处理器是单线程的,然而当初的CPU基本上都是多核的,为了充分利用古代CPU多核的个性,咱们能够创立cluster,从而使多个子过程来共享同一个服务器端口。
也就是说,通过cluster,咱们能够应用多个子过程来服务解决同一个端口的申请。
先看一个简略的http server中应用cluster的例子:
const cluster = require('cluster');const http = require('http');const numCPUs = require('os').cpus().length;if (cluster.isMaster) { console.log(`主过程 ${process.pid} 正在运行`); // 衍生工作过程。 for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`工作过程 ${worker.process.pid} 已退出`); });} else { // 工作过程能够共享任何 TCP 连贯。 // 在本例子中,共享的是 HTTP 服务器。 http.createServer((req, res) => { res.writeHead(200); res.end('你好世界\n'); }).listen(8000); console.log(`工作过程 ${process.pid} 已启动`);}
cluster详解
cluster模块源自于lib/cluster.js,咱们能够通过cluster.fork()来创立子工作过程,用来解决主过程的申请。
cluster中的event
cluster继承自events.EventEmitter,所以cluster能够发送和接管event。
cluster反对7中event,别离是disconnect,exit,fork,listening,message,online和setup。
在解说disconnect之前,咱们先介绍一个概念叫做IPC,IPC的全称是Inter-Process Communication,也就是过程间通信。
IPC次要用来进行主过程和子过程之间的通信。一个工作过程在创立后会主动连贯到它的主过程。 当 'disconnect' 事件被触发时才会断开连接。
触发disconnect事件的起因有很多,能够是被动调用worker.disconnect(),也能够是工作过程退出或者被kill掉。
cluster.on('disconnect', (worker) => { console.log(`工作过程 #${worker.id} 已断开连接`);});
exit事件会在任何一个工作过程敞开的时候触发。个别用来监测cluster中某一个过程是否异样退出,如果退出的话应用cluster.fork创立新的过程,以保障有足够多的过程来解决申请。
cluster.on('exit', (worker, code, signal) => { console.log('工作过程 %d 敞开 (%s). 重启中...', worker.process.pid, signal || code); cluster.fork();});
fork事件会在调用cluster.fork办法的时候被触发。
const timeouts = [];function errorMsg() { console.error('连贯出错');}cluster.on('fork', (worker) => { timeouts[worker.id] = setTimeout(errorMsg, 2000);});
主过程和工作过程的listening事件都会在工作过程调用listen办法的时候触发。
cluster.on('listening', (worker, address) => { console.log( `工作过程已连贯到 ${address.address}:${address.port}`);});
其中worker代表的是工作线程,而address中蕴含三个属性:address、 port 和 addressType。 其中addressType有四个可选值:
- 4 (TCPv4)
- 6 (TCPv6)
- -1 (Unix 域 socket)
- 'udp4' or 'udp6' (UDP v4 或 v6)
message事件会在主过程收到子过程发送的音讯时候触发。
当主过程生成工作过程时会触发fork,当工作过程运行时会触发online。
setupMaster办法被调用的时候,会触发setup事件。
cluster中的办法
cluster中三个办法,别离是disconnect,fork和setupMaster。
cluster.disconnect([callback])
调用cluster的disconnect办法,实际上会在cluster中的每个worker中调用disconnect办法。从而断开worker和主过程的连贯。
当所有的worker都断开连接之后,会执行callback。
cluster.fork([env])
fork办法,会从主过程中创立新的子过程。其中env是要增加到过程环境变量的键值对。
fork将会返回一个cluster.Worker对象,代表工作过程。
最初一个办法是setupMaster:
cluster.setupMaster([settings])
默认状况下,cluster通过fork办法来创立子过程,然而咱们能够通过setupMaster来扭转这个行为。通过设置settings变量,咱们能够扭转前面fork子过程的行为。
咱们看一个setupMaster的例子:
const cluster = require('cluster');cluster.setupMaster({ exec: 'worker.js', args: ['--use', 'https'], silent: true});cluster.fork(); // https 工作过程cluster.setupMaster({ exec: 'worker.js', args: ['--use', 'http']});cluster.fork(); // http 工作过程
cluster中的属性
通过cluster对象,咱们能够通过isMaster和isWorker来判断过程是否主过程。
能够通过worker来获取当前工作过程对象的援用:
const cluster = require('cluster');if (cluster.isMaster) { console.log('这是主过程'); cluster.fork(); cluster.fork();} else if (cluster.isWorker) { console.log(`这是工作过程 #${cluster.worker.id}`);}
能够通过workers来遍历沉闷的工作过程对象:
// 遍历所有工作过程。function eachWorker(callback) { for (const id in cluster.workers) { callback(cluster.workers[id]); }}eachWorker((worker) => { worker.send('告诉所有工作过程');});
每个worker都有一个id编号,用来定位该worker。
cluster中的worker
worker类中蕴含了对于工作过程的所有的公共的信息和办法。cluster.fork进去的就是worker对象。
worker的事件和cluster的很相似,反对6个事件:disconnect,error,exit,listening,message和online。
worker中蕴含3个属性,别离是:id,process和exitedAfterDisconnect。
其中id是worker的惟一标记。
worker中的process,实际上是ChildProcess对象,是通过child_process.fork()来创立进去的。
因为在worker中,process属于全局变量,所以咱们能够间接在worker中应用process来进行发送音讯。
exitedAfterDisconnect示意如果工作过程因为 .kill() 或 .disconnect() 而退出的话,值就是true。如果是以其余形式退出的话,返回值就是false。如果工作过程尚未退出,则为 undefined。
咱们能够通过worker.exitedAfterDisconnect 来辨别是被动退出还是被动退出,主过程能够依据这个值决定是否从新生成工作过程。
cluster.on('exit', (worker, code, signal) => { if (worker.exitedAfterDisconnect === true) { console.log('这是自发退出,无需放心'); }});// 杀死工作过程。worker.kill();
worker还反对6个办法,别离是:send,kill,destroy,disconnect,isConnected,isDead。
这里咱们次要解说一下send办法来发送音讯:
worker.send(message[, sendHandle[, options]][, callback])
能够看到send办法和child_process中的send办法参数其实是很相似的。而实质上,worker.send在主过程中,这会发送音讯给特定的工作过程。 相当于 ChildProcess.send()。在工作过程中,这会发送音讯给主过程。 相当于 process.send()。
if (cluster.isMaster) { const worker = cluster.fork(); worker.send('你好');} else if (cluster.isWorker) { process.on('message', (msg) => { process.send(msg); });}
在下面的例子中,如果是在主过程中,那么能够应用worker.send来发送音讯。而在子过程中,则能够应用worker中的全局变量process来发送音讯。
总结
应用cluster能够充沛应用多核CPU的劣势,心愿大家在理论的我的项目中利用起来。
本文作者:flydean程序那些事本文链接:http://www.flydean.com/nodejs-cluster/
本文起源:flydean的博客
欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!