Node.js Cluster模块核心机制与实战指南

作者:性能诊所时间:2026-10-24 20:57:14浏览:230

本文深入解析Node.js Cluster模块,阐述其利用多核CPU实现负载均衡的机制。涵盖主进程与Worker进程的通信、进程监控与自动重启、worker对象属性方法,以及结合PM2进行生产环境部署的最佳实践,帮助开发者构建高可用Node应用。

在面试中,当被问及Node.js如何实现多进程处理时,正确答案应指向Cluster模块。接下来,我们将深入探讨该模块的具体使用方式。

基本用法

Node.js默认采用单进程模型运行。在32位系统中,内存上限约为512MB;在64位系统中,上限约为1GB。面对多核CPU计算机,单进程运行会导致其他核心闲置,效率低下。Cluster模块正是为了解决这一资源利用率问题而设计。

该模块允许创建一个主进程(Master)和多个工作进程(Worker)。主进程负责监控和协调Worker进程的运行。Worker进程之间通过进程间通信交换消息,且Cluster模块内置了基于Round-robin(轮询)算法的负载均衡器,以协调各Worker间的负载。在运行时,所有新建立的链接首先由主进程接收,随后主进程将TCP连接分配给指定的Worker进程。


var cluster = require('cluster');
var os = require('os');

if (cluster.isMaster){
for (var i = 0, n = os.cpus().length; i < n; i += 1){
cluster.fork();
}
} else {
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}

上述代码首先判断当前进程是否为主进程(通过cluster.isMaster)。如果是主进程,则根据CPU核数创建若干个Worker进程;如果不是,则表明当前为Worker进程,此时在该进程中启动服务器程序。

上述代码存在一个缺陷:一旦Worker进程崩溃,主进程无法感知。为解决此问题,需要在主进程中部署对online事件和exit事件的监听函数。


var cluster = require('cluster');
 
if(cluster.isMaster) {
  var numWorkers = require('os').cpus().length;
  console.log('Master cluster setting up ' + numWorkers + ' workers...');
 
  for(var i = 0; i < numWorkers; i++) {
    cluster.fork();
  }
 
  cluster.on('online', function(worker) {
    console.log('Worker ' + worker.process.pid + ' is online');
  });
 
  cluster.on('exit', function(worker, code, signal) {
    console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);
    console.log('Starting a new worker');
    cluster.fork();
  });
}

在主进程代码中,一旦监听到Worker进程的exit事件,便会立即重启一个新的Worker进程。当Worker进程启动成功并正常运行时,会触发online事件。

worker对象

worker对象是cluster.fork()方法的返回值,代表一个具体的Worker进程。

其包含以下属性和方法。

(1)worker.id

worker.id返回当前Worker的唯一进程编号。该编号同时也是cluster.workers对象中指向当前进程的索引值。

(2)worker.process

所有Worker进程均通过child_process.fork()生成。该fork方法返回的对象保存在worker.process属性中。通过此属性,可以获取Worker所在的进程对象。

(3)worker.send()

该方法用于在主进程中向子进程发送信息。


if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send('hi there');
} else if (cluster.isWorker) {
  process.on('message', function(msg) {
    process.send(msg);
  });
}

上述代码的作用是,让Worker进程对主进程发出的每条消息进行回声处理。

在Worker进程中,若要向主进程发送消息,需使用process.send(message);若要监听主进程发出的消息,可使用如下代码。


process.on('message', function(message) {
  console.log(message);
});

发送的消息可以是字符串,也可以是JSON对象。以下是一个发送JSON对象的示例。


worker.send({
  type: 'task 1',
  from: 'master',
  data: {
    // the data that you want to transfer
  }
});

cluster.workers对象

该对象仅在主进程中可用,包含所有Worker进程。其每个成员的键值是一个Worker进程对象,键名即为该Worker进程的worker.id属性值。


socket.on('data', function(id) {
  var worker = cluster.workers[id];
});

cluster模块的属性与方法

isMaster,isWorker

isMaster属性返回布尔值,表示当前进程是否为主进程。该属性由process.env.NODE_UNIQUE_ID决定:若该环境变量未定义,则表明当前进程为主进程。

isWorker属性返回布尔值,表示当前进程是否为Worker进程。其值与isMaster属性正好相反。

fork()

fork方法用于新建一个Worker进程,其上下文复制自主进程。只有主进程能调用此方法。

该方法返回一个worker对象。

kill()

kill方法用于终止Worker进程。它可接受一个参数,表示系统信号。

若当前为主进程,该方法将终止与worker.process的联系,并将系统信号发送给Worker进程。若当前为Worker进程,则终止与主进程的通信,然后退出并返回0。

在早期版本中,该方法也被称为worker.destroy()。

listening事件

当Worker进程调用listening方法后,“listening”事件会传递至该进程的服务器,进而传递至主进程。

该事件的回调函数接收两个参数:当前Worker对象和地址对象。地址对象包含网址、端口、地址类型(IPv4、IPv6、Unix socket、UDP)等信息。这对于服务多个网址的Node应用程序非常有用。

不中断地重启Node服务

重启服务通常涉及关闭后再启动。利用Cluster模块,可以实现先启动一个新的Worker进程,再关闭所有原有Worker进程,从而做到不中断地重启Node服务。

首先,主进程向Worker进程发出重启信号。


workers[wid].send({type: 'shutdown', from: 'master'});

Worker进程监听message事件,一旦发现消息内容为shutdown,便执行退出操作。


process.on('message', function(message) {
  if(message.type === 'shutdown') {
    process.exit(0);
  }
});

以下是一个关闭所有Worker进程的函数示例。


function restartWorkers() {
  var wid, workerIds = [];
  for(wid in cluster.workers) {
    workerIds.push(wid);
  }
 
  workerIds.forEach(function(wid) {
    cluster.workers[wid].send({
      text: 'shutdown',
      from: 'master'
     });
    setTimeout(function() {
      if(cluster.workers[wid]) {
        cluster.workers[wid].kill('SIGKILL');
      }
    }, 5000);
  });
};

PM2模块

PM2模块是Cluster模块的一个封装层。其目的是尽可能抽象掉Cluster模块的复杂性,让用户能够像使用单进程一样部署多进程Node应用。


// app.js
var http = require('http');
 
http.createServer(function(req, res) {
  res.writeHead(200);
  res.end("hello world");
}).listen(8080);

通过PM2从命令行启动上述代码。


$ pm2 start app.js -i 4

上述代码中的-i参数告知PM2,该代码应在cluster_mode下启动,且新建Worker进程的数量为4个。若-i参数值为0,则PM2会根据当前机器的CPU内核数量启动相应数量的Worker进程。

如果一个Worker进程因某种原因挂掉,PM2会立即重启该Worker进程。


# 重启所有worker进程
$ pm2 reload all

每个Worker进程都有一个id,可以使用以下命令查看单个Worker进程的详细信息。


$ pm2 show <worker id>

在关闭Worker进程时,可以部署以下代码,让Worker进程监听shutdown消息。一旦收到该消息,在完成收尾清理工作后再关闭。


process.on('message', function(msg) {
  if (msg === 'shutdown') {
    close_all_connections();
    delete_logs();
    server.close();
    process.exit(0);
  }
});

至此,关于Node.js Cluster模块的知识点总结及实例用法介绍完毕。更多相关Cluster模块介绍内容,请搜索本站以前的文章或继续浏览下面的相关文章。希望大家以后多多支持本站!

作者最新文章
相关文章
更多