简介
在后面的文章中,咱们讲到了能够通过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的博客
欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
发表回复