cluster
集群
单个Nodejs实例运行在单个线程中,为充分利用多核系统,需要启用一组Node进程处理负载任务。
cluster允许建立一个主进程和若干个worker进程,由主进程监控和协调worker进程的运行。
worker之间采用进程通信交换消息,cluster模块内置一个负载均衡。
cluster集成两个方面:
- 集成了child_process.fork方法创建node子进程的方式;
- 继承了很多多核cpu创建子进程后,自动控制负载均衡的方式;
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} 已启动`);
}
主进程 3596 正在运行
工作进程 4324 已启动
工作进程 4520 已启动
工作进程 6056 已启动
工作进程 5644 已启动
工作原理
cluster支持两种分发连接方法:
- 循环法,由主进程负责监听端口,接收新连接后再将连接循环发给工作进程,在分发中使用了一些内置技巧防止工作进程中任务过载;
- 主进程创建监听socket后发送给感兴趣的工作进程,由工作进程负责直接接收连接;
Worker类
Worker对象包含了关于工作进程的所有的公共的信息和方法。
主进程中,使用cluster.workers获取
工作进程中,使用cluster.worker获取
disconnect事件
断开连接
error事件
工作进程中,可以使用process.on(‘error’)
exit事件
const worker = cluster.fork();
worker.on('exit', (code, signal) => {
if (signal) {
console.log(`工作进程已被信号 ${signal} 杀死`);
} else if (code !== 0) {
console.log(`工作进程退出,退出码: ${code}`);
} else {
console.log('工作进程成功退出');
}
});
listening事件
cluster.fork().on('listening', (address) => {
// 工作进程正在监听。
});
message事件
在工作进程内,也可以使用 process.on(‘message’)
online事件
cluster.fork().on('online', () => {
// 工作进程已上线。
});
worker.disconnect()
在一个工作进程内,调用此方法会关闭所有的 server,并等待这些 server 的 ‘close’ 事件执行,然后关闭 IPC 管道。
在主进程内,会给工作进程发送一个内部消息,导致工作进程自身调用 .disconnect()
worker.exitedAfterDisconnect
如果工作进程由于 .kill() 或 .disconnect() 而退出,则此属性为 true。
如果工作进程以任何其他方式退出,则为 false。
如果工作进程尚未退出,则为 undefined。
cluster.on('exit', (worker, code, signal) => {
if (worker.exitedAfterDisconnect === true) {
console.log('这是自发退出,无需担心');
}
});
// 杀死工作进程。
worker.kill();
worker.id
每一个新衍生的工作进程都会被赋予自己独一无二的编号,这个编号就是储存在 id 里面。
worker.isConnected()
当工作进程通过 IPC 管道连接至主进程时,这个方法返回 true,否则返回 false。
一个工作进程在创建后会自动连接到它的主进程。
当 ‘disconnect’ 事件被触发时才会断开连接。
worker.isDead()
当工作进程被终止时(包括自动退出或被发送信号),这个方法返回 true。 否则,返回 false。
worker.kill([signal=‘SIGTERM’])
杀死工作进程。
主进程,通过断开与worker.process的连接来实现,一旦断开连接,通过signal杀死工作进程;
工作进程,通过断开IPC管道来实现,然后以代码0退出进程;
worker.process
所有的工作都通过child_process.fork()创建,返回的对象被存储为.process。工作进程中,process属于全局对象。
当process发生disconnect事件,且.exitedAfterDisconnect的值不是true时,工作进程会调用process.exit(0)来防止连接意外断开。
worker.send()
发送消息给主进程或工作进程。
if (cluster.isMaster) {
const worker = cluster.fork();
worker.send('你好');
} else if (cluster.isWorker) {
process.on('message', (msg) => {
process.send(msg);
});
}
disconnect事件
工作进程的IPC管道被断开后触发。
可能导致触发的原因:
- 工作进程优雅的退出;
- 被杀死;
- 手动断开连接;
‘disconnect’ 和 ‘exit’ 事件之间可能存在延迟。 这些事件可以用来检测进程是否在清理过程中被卡住,或是否存在长时间运行的连接。
cluster.on('disconnect', (worker) => {
console.log(`工作进程 #${worker.id} 已断开连接`);
});
exit事件
当任何一个工作进程关闭的时候,cluster模块都会触发exit事件。
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);
});
cluster.on('listening', (worker, address) => {
clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', (worker, code, signal) => {
clearTimeout(timeouts[worker.id]);
errorMsg();
});
listening事件
当工作进程调用listen()后,工作进程上的server会触发listening事件,
同时主进程上的cluster会触发listening事件。
worker 包含了工作进程对象
address 包含了以下的连接属性:address、 port 和 addressType。
cluster.on('listening', (worker, address) => {
console.log(
`工作进程已连接到 ${address.address}:${address.port}`);
});
addressType 可选值包括:
- 4 (TCPv4)
- 6 (TCPv6)
- -1 (Unix 域 socket)
- ‘udp4’ or ‘udp6’ (UDP v4 或 v6)
message事件
当集群主进程从任何工作进程接收到消息时触发。
online事件
当衍生一个新的工作进程后,工作进程会响应一个上线消息。
当主进程收到上线消息后悔触发此事件。
当主进程衍生工作进程时触发fork;
当工作进程运行时触发online;
cluster.on('online', (worker) => {
console.log('工作进程被衍生后响应');
});
setup事件
每当.setupMaster()被调用时触发。
cluster.disconnect()
cluster.workers的每个工作进程中调用.disconnect()
cluster.fork()
衍生出一个新的工作进程,只能通过主进程调用。
cluster.isMaster
如果进程是主进程,则为true。
cluster.isWorker
如果进程不是主进程,则为true。
cluster.schdulingPolicy
调度策略。
cluster.settings
调用.setupMaster()或.fork()之后,这个配置对象将会包含这些配置项,包括默认值。
cluster.setupMaster()
setupMaster用于修改默认的fork行为,一旦调用,会按照cluster.settings进行设置。
所有的设置只对后来的.fork()调用有效,对之前的工作进程无影响。
唯一无法通过.setupMaster()设置的属性是传给.fork()的env属性。
只能由主进程调用。
上述的默认值只在第一次调用时有效,当后续调用时,将采用 cluster.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.worker
当前工作进程对象的引用,对于主进程是无用的。
const cluster = require('cluster');
if (cluster.isMaster) {
console.log('这是主进程');
cluster.fork();
cluster.fork();
} else if (cluster.isWorker) {
console.log(`这是工作进程 #${cluster.worker.id}`);
}
cluster.workers
一个哈希表,储存了活跃的工作进程对象,id作为键名。
只能在主进程中调用。
工作进程断开连接及退出后,将会从cluster.workers里面移除。
移除工作在disconnect和exit两个事件中的最后一个触发之前完成。
// 遍历所有工作进程。
function eachWorker(callback) {
for (const id in cluster.workers) {
callback(cluster.workers[id]);
}
}
eachWorker((worker) => {
worker.send('通知所有工作进程');
});
使用工作进程的唯一 id 是定位工作进程最简单的方式。
socket.on('data', (id) => {
const worker = cluster.workers[id];
});
来源:CSDN
作者:程序媛小小平
链接:https://blog.csdn.net/mo_247/article/details/103925674