上手指南
有了这个模块,你会感觉实现Node的单机集群是多么容易的一件事情。下面看看官方实例,短短的十几行代码就实现了一个多进程的Node服务,且自带负载均衡。
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('hello world\n'); }).listen(8000); console.log(`工作进程 ${process.pid} 已启动`); }
cluster模块源码分析
首先看代码,通过isMaster
来判断是否为主进程,如果是主进程进行fork操作,子进程创建服务器。这里cluster进行fork操作时,执行的是当前文件。cluster.fork
最终调用的child_process.fork
,且第一个参数为process.argv.slice(2)
,在fork子进程之后,会对其internalMessage事件进行监听,这个后面会提到,具体代码如下:
const { fork } = require('child_process'); cluster.fork = function(env) { cluster.setupMaster(); const id = ++ids; const workerProcess = createWorkerProcess(id, env); const worker = new Worker({ id: id, process: workerProcess }); // 监听子进程的消息 worker.process.on('internalMessage', internal(worker, onmessage)); // ... }; // 配置master进程 cluster.setupMaster = function(options) { cluster.settings = { args: process.argv.slice(2), exec: process.argv[1], execArgv: process.execArgv, silent: false, ...cluster.settings, ...options }; }; // 创建子进程 function createWorkerProcess(id, env) { return fork(cluster.settings.exec, cluster.settings.args, { // some options }); }
子进程端口监听问题
这里会有一个问题,子进程全部都在监听同一个端口,我们之前已经试验过,服务监听同一个端口会出现端口占用的问题,那么cluster模块如何保证端口不冲突的呢? 查阅源码发现,http模块的createServer继承自net模块。
util.inherits(Server, net.Server);
而在net模块中,listen方法会调用listenInCluster方法,listenInCluster判断当前是否为master进程。