Node.js集群模块cluster之集群类cluster介绍

 2015年11月27日    948     声明


每个Node实例都在单线程环境中运行,这就导致程序不能有效利用多核系统资源。为了更好的利用多核系统,可以使用cluster模块启动一个Node集群来处理负载。

  1. cluster类中的事件
  2. cluster类中的属性
  3. cluster类中的方法

本文将详细介绍cluster模块中类、事件、方法等,cluster模块使用示例请参考:cluster模块让Node.js充分利用多核资源

cluster模块是一个EventEmitter实例,所以它会有一些事件,如:'exit''fork'等。cluster模块创建进程集群时,分为主进程cluster对象和工作进程Worker对象,Worker工作进程类是cluster主进程类的子类。


1. cluster类中的事件

1.1 事件:'disconnect'

  • worker:{Worker object}

当工作进程的 IPC 通道关闭时会触发'disconnect'事件,包括:工作进程正常退出、被杀死、或者手工关闭(如:worker.disconnect())都触发这个事件。这个事件的回调函数中有一个参数,该参数是被关闭的worker实例。

'disconnect'事件和'exit'事件之间存在延迟,通过这两个事件可以查明进程退出的原因,如,进程被清理或是异常:

cluster.on('disconnect', function(worker) {
  console.log('工作进程 #' + worker.id + ' 断开了连接');
});


1.2 事件:'exit'

  • worker {Worker object}
  • code {Number} 如果正常退出,则为退出代码.
  • signal {String} 进程退出的信号名(如:'SIGHUP'

当任意工作进程被结束时,集群模块会触发'exit'事件,其回调函数中有三个参数,分别是:workercodesignal。如果需要对进程进行重启,可以使用fork()方法。

cluster.on('exit', function(worker, code, signal) {
  console.log('工作进程 %d 结束 (%s)。重启中...',
    worker.process.pid, signal || code);
  cluster.fork();
});


1.3 事件:'fork'

  • worker {Worker object}

当一个新的工作进程被创建时,cluster对象会产生一个'fork'事件,此事件可用于记录工作进程活动,创建自己所需的超时判断。

var timeouts = [];
function errorMsg() {
  console.error("连接出了问题 ...");
}

cluster.on('fork', function(worker) {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', function(worker, address) {
  clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', function(worker, code, signal) {
  clearTimeout(timeouts[worker.id]);
  errorMsg();
});


1.4 事件:'listening'

  • worker {Worker object}
  • address {Object}

当工作进程调用listen()方法时,服务器和cluster主进程会触发'listening'事件。

事件处理函数有两个参数,工作进程对象workeraddressaddress包含以下属性:addressportaddressType。当工作进程监听多个地址的时候,这些参数会非常有用。其中addressType可能包含以下值之一:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (unix domain socket)
  • "udp4" or "udp6" (UDP v4 or v6)*
cluster.on('listening', function(worker, address) {
  console.log("一个worker,连接到了:" + address.address + ":" + address.port);
});


1.5 事件:'message'

  • worker {Worker object}
  • message {Object}

任何一个worker对象收到消息时,会触发此事件


1.6 事件:'online'

  • worker {Worker object}

当分支出一个新的工作进程后,工作进程会响应一个在线消息,主进程收到这个在线消息后,它会触发该事件。'fork''online'的区别在于前者发生于主进程尝试分支出工作进程时,而后者发生于工作进程被执行时。

cluster.on('online', function(worker) {
  console.log("工作进程分支完成并发出回应了");
});


1.7 事件:'setup'

  • settings {Object}

.setupMaster() 函数被执行时触发此事件。如果 .setupMaster()fork() 之前没被执行,那么它会不带参数调用 .setupMaster()settings对象是一个cluster.settings对象。


2. cluster类中的属性

2.1 是否主进程:cluster.isMaster

  • 返回值:{Boolean}

如果进程为主进程则为true。这是由 process.env.NODE_UNIQUE_ID 判断的,如果 process.env.NODE_UNIQUE_ID 为 undefined,则 isMastertrue


2.2 是否子进程:cluster.isWorker

  • 返回值:{Boolean}

如果不是主进程则返回 true (和cluster.isMaster 相反)。


2.3 调度策略:cluster.schedulingPolicy

调度策略 cluster.SCHED_RR 表示轮流制,cluster.SCHED_NONE 表示由操作系统处理。这是一个全局设定,并且一旦您派生了第一个工作进程或调用了 cluster.setupMaster() 后便不可更改。 SCHED_RR 是除 Windows 外所有操作系统上的缺省方式。只要 libuv 能够有效地分配 IOCP 句柄并且不产生巨大的性能损失,Windows 也将会更改为 SCHED_RR 方式。

cluster.schedulingPolicy 也可以通过环境变量 NODE_CLUSTER_SCHED_POLICY 设定。有效值为 "rr""none"


2.4 设置对象:cluster.settings

  • 返回值:{Object}
    • execArgv {Array} 传给可执行的 Node 的参数列表(默认:process.execArgv)
    • exec {String} 工作进程文件的路径(默认:__filename
    • args {Array} 传递给工作进程的字符串参数。(缺省为 process.argv.slice(2)
    • silent {Boolean} 是否将输出发送到父进程的 stdio。(缺省为 false
    • uid {Number} 设置用户进程的ID
    • gid {Number} 设置进程组的ID

调用 .setupMaster() (或 .fork()) 方法后,这个settings对象会包含设置内容。,settings不应该手工修改,而应该通过方法调用设置,.setupMaster()只能调用一次


2.5 工作进程对象:cluster.worker

  • 返回值:{Object}

对当前工作进程对象的引用,在主进程中不可用。

var cluster = require('cluster');

if (cluster.isMaster) {
  console.log('我是主进程');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log('我是工作进程 #' + cluster.worker.id);
}


2.6 所有工作进程对象:cluster.workers

  • 返回值:{Object}

一个储存活动工作进程对象的哈希表,以id字段作为主键。它能被用作遍历所有工作进程,仅在主进程中可用。

当工作进程关闭连接并退出后,将会从cluster.workers中移除。由于'disconnect''exit'两个事件的次序无法确定,从cluster.workers中移除发生在这两个事件之后。

// 遍历所有工作进程
function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker(function(worker) {
  worker.send('向一线工作者们致以亲切问候!');
});

如果你要通过通讯通道引用一个工作进程,那么使用工作进程的唯一标识是找到那个工作进程的最简单的办法。

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


3. cluster类中的方法

3.1 关闭所有工作进程:cluster.disconnect()

cluster.disconnect([callback])
  • callback:{Function},回调函数,当所有工作进程都断开连接,并且句柄关闭后调用

调用此方法时,所有的工作进程都会优雅地退出。当它们都断开连接时,所有的内部处理器都会被关闭,使得主进程可以在没有其它事件等待时优雅地结束。

此方法会对cluster.workers 里的每个工作进程,调用.disconnect()方法进行关闭。


3.2 创建新的工作进程:cluster.fork()

cluster.fork([env])
  • env:{Object},添加到子进程环境变量中的键值
  • 返回值:{Worker object}

.fork()方法用于派生一个新的工作进程,这个方法只能在主进程中调用。


3.2 修改fork设置:cluster.setupMaster()

cluster.setupMaster([settings])
  • settings:{Object}
    • exec {String} 工作进程文件的路径。(默认:__filename
    • args {Array} 传给工作进程的字符串参数。(默认:process.argv.slice(2)
    • silent {Boolean} 是否将输出发送到父进程的 stdio。(默认:false

setupMaster 被用于设置的fork新工作进程时的默认值。新的设置会立即永久生效,并且在之后不能再次被更改。该方法只能在主进程中调用。

var cluster = require('cluster');
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // https worker
cluster.setupMaster({
  args: ['--use', 'http']
});
cluster.fork(); // http worker