Node.js的进程管理的深入理解(5)

上手指南

有了这个模块,你会感觉实现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进程。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/242.html