开发手册 欢迎您!
软件开发者资料库

集群(Cluster) | Node.js

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。

Node.js v8.x 中文文档


cluster (集群)#

稳定性: 2 - 稳定的

Node.js在单个线程中运行单个实例。用户(开发者)为了使用现在的多核系统,有时候,用户(开发者)会用一串Node.js进程去处理负载任务。

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} 已启动`);}

现在运行 Node.js 将会在工作进程(指代子进程)之间共享8000端口

$ node server.js主进程 3596 正在运行工作进程 4324 已启动工作进程 4520 已启动工作进程 6056 已启动工作进程 5644 已启动

请注意,在Windows中,还不能在工作进程中设置管道(Pipe)服务器。

How It Works#

工作进程由child_process.fork()方法创建,因此它们可以使用IPC和父进程通信,从而使各进程交替处理连接服务。

cluster模块支持两种连接分发模式(将新连接安排给某一工作进程处理)。

第一种方法(也是除Windows外所有平台的默认方法),是循环法。由主进程负责监听端口,接收新连接后再将连接循环分发给工作进程。在分发中使用了一些内置技巧防止工作进程任务过载。

第二种方法是,主进程创建监听socket后发送给感兴趣的工作进程,由工作进程负责直接接收连接。

理论上第二种方法应该是效率最佳的,但在实际情况下,由于操作系统调度机制的难以捉摸,会使分发变得不稳定。我们遇到过这种情况:8个进程中的2个,分担了70%的负载。

因为server.listen()将大部分工作交给主进程完成,因此导致普通Node.js进程与cluster作业进程差异的情况有三种:

  1. server.listen({fd: 7})由于文件描述符“7”是传递给父进程的,这个文件被监听后,将文件句柄(handle)传递给工作进程,而不是文件描述符“7”本身。
  2. server.listen(handle) 明确监听句柄,会导致工作进程直接使用该句柄,而不是和父进程通信。
  3. server.listen(0) 正常情况下,这种调用会导致server在随机端口上监听。但在cluster模式中,所有工作进程每次调用listen(0)时会收到相同的“随机”端口。实质上,这种端口只在第一次分配时随机,之后就变得可预料。如果要使用独立端口的话,应该根据工作进程的ID来生成端口号。

注意:Node.js不支持路由逻辑。因此在设计应用时,不应该过分依赖内存数据对象(如sessions和login等)。

由于各工作进程是独立的进程,它们可以根据需要随时关闭或重新生成,而不影响其他进程的正常运行。只要有存活的工作进程,服务器就可以继续处理连接。如果没有存活的工作进程,现有连接会丢失,新的连接也会被拒绝。Node.js不会自动管理工作进程的数量,而应该由具体的应用根据实际需要来管理进程池。

Although a primary use case for the cluster module is networking, it canalso be used for other use cases requiring worker processes.

Class: Worker#

Worker对象包含了关于工作进程的所有public信息和方法。

在一个主进程里,可以使用cluster.workers来获取Worker对象。

在一个工作进程里,可以使用cluster.worker来获取Worker对象。

Event: 'disconnect'#

虽然与 cluster.on('disconnect')事件 是相似的,但是这个进程又有其他特征。

cluster.fork().on('disconnect', () => {  // Worker has disconnected});

Event: 'error'#

此事件和 child_process.fork()提供的error事件相同。

在一个工作进程中,可以使用process.on('error')

Event: 'exit'#

  • code 若正常退出,表示退出代码.
  • signal 引发进程被kill的信号名称(如'SIGHUP').

cluster.on('exit')事件类似,但针对特定的工作进程。

const worker = cluster.fork();worker.on('exit', (code, signal) => {  if (signal) {    console.log(`worker was killed by signal: ${signal}`);  } else if (code !== 0) {    console.log(`worker exited with error code: ${code}`);  } else {    console.log('worker success!');  }});

Event: 'listening'#