共计 4463 个字符,预计需要花费 12 分钟才能阅读完成。
简介
在后面的文章中,咱们讲到了能够通过 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 的博客
欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!