Node.js v0.10.31API手册-集群

集群

单个 Node 实例运行在单个线程中。要发挥多核系统的能力,用户有时候需要启动一个 Node 进程集群来处理负载。

集群模块允许你方便地创建一个共享服务器端口的进程网络。

var cluster = require(‘cluster‘);
var http = require(‘http‘);
var numCPUs = require(‘os‘).cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on(‘exit‘, function(worker, code, signal) {
    console.log(‘worker ‘ + worker.process.pid + ‘ died‘);
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

现在,运行 node 将会在所有工作进程间共享 8000 端口:

% NODE_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 online

这是一个近期推出的功能,在未来版本中可能会有所改变。请尝试并提供反馈。

还要注意的是,在
Windows 中尚不能在工作进程中建立一个被命名的管道服务器。

它是如何工作的

工作进程是通过使用 child_process.fork 方法派生的,因此它们可以通过
IPC(进程间通讯)与父进程通讯并互相传递服务器句柄。

集群模块支持两种分配传入连接的方式。

当你在一个工作进程中调用server.listen(...) 时,它序列化参数并且将请求传递给主进程。若主进程已经监听匹配工作进程的服务,它将向工作进程发送该句柄。若主进程没有监听配工作进程的服务,那么就创建该服务并将句柄发送给工作进程。

这种模式导致了在一下三种情况下令人惊讶的潜在行为:

  1. server.listen({fd: 7}) 由于消息被传递到主进程,父进程中的文件描述符 7 会被监听,并且句柄会被传递给工作进程,而不是监听工作进程中文件描述符 7 所引用的东西。
  2. server.listen(handle) 明确地监听一个句柄会使得工作进程使用所给句柄,而不是与主进程通讯。如果工作进程已经拥有了该句柄,则假定您知道您在做什么。
  3. server.listen(0) 通常,这会让服务器监听一个随机端口。然而,在集群中,各个工作进程每次 listen(0)都会得到一样的“随机”端口。实际上,端口在第一次时是随机的,但在那之后却是可预知的。如果您想要监听一个唯一的端口,则请根据集群工作进程
    ID 来生成端口号。

当多个进程都在accept()同一潜在资源时,操作系统的负载均衡是非常有效的。由于在
Node.js 或您的程序中并没有路由逻辑,工作进程之间也没有共享的状态,因此在您的程序中,诸如会话和登录等功能应当被设计成不能太过依赖于内存中的数据对象。

由于工作进程都是独立的进程,因此它们会根据您的程序的需要被终止或重新派生,并且不会影响到其它工作进程。只要还有工作进程存在,服务器就会继续接受连接。但是,Node
不会自动为您管理工作进程的数量,根据您的程序所需管理工作进程池是您的责任。

cluster.settings

  • {Object}

    • execArgv将字符串参数 Array 列表传递给node可执行文件。(默认为process.execArgv)

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

当完成调用 .setupMaster() (或者 .fork())时,
这个setting对象将包含设置,包括默认值。

由于.setupMaster() 仅能被调一次,所以这种行为在设置后有效的冻结了函数。

这个对象不支持变更和手动设置。

cluster.isMaster

  • {Boolean}

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

cluster.isWorker

  • Boolean

若非主进程为true。(与cluster.isMaster相反)

Event: ‘fork‘

  • worker Worker
    object

当一个新的工作进程被分支出来,cluster 模块会发送一个 ‘fork‘ 事件。这可被用于记录工作进程活动,以及创建您自己的超时判断。

var timeouts = [];
function errorMsg() {
  console.error("Something must be wrong with the connection ...");
}

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();
});

Event: ‘online‘

  • worker Worker
    object

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

cluster.on(‘online‘, function(worker) {
  console.log("Yay, the worker responded after it was forked");
});

Event: ‘listening‘

  • worker Worker
    object
  • address Object

当工作进程调用 listen() 时,一个 listening 事件会被自动分配到服务器实例中。当服务器处于监听时,一个消息会被发送到那个‘listening‘事件被分发的主进程。

事件处理器被执行时会带上两个参数。其中 worker 包含了工作进程对象,address 对象包含了下列连接属性:地址 address、端口号 port 和地址类型 addressType。如果工作进程监听多个地址,那么这些信息将十分有用。

cluster.on(‘listening‘, function(worker, address) {
  console.log("一个工作进程刚刚了连接到" + address.address + ":" + address.port);
});

地址类型addressType 为一下之一:

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (unix 域套接字)
  • "udp4"或者"udp6" (UDP
    v4 或者 v6)

Event: ‘disconnect‘

  • worker Worker
    object

当一个工作进程的 IPC 通道断开时此事件会发生。这发生于工作进程正常退出时,通常是调用 .kill() 或者被手动的断开连接之后。

当调用 .disconnect() 后,disconnect 和 exit 事件之间可能存在延迟。该事件可被用于检测进程是否被卡在清理过程或存在长连接。

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

Event: ‘exit‘

  • worker {Worker object}
  • code {Number} 如果是正常退出则为退出代码。
  • signal {String} 使得进程被终止的信号的名称(比如 ‘SIGHUP‘)。

当任意工作进程被结束时,集群模块会分发exit 事件。

通过再次调用fork()函数,可以使用这个事件来重启工作进程。

cluster.on(‘exit‘, function(worker, code, signal) {
  console.log(‘worker %d died (%s). restarting...‘,
    worker.process.pid, signal || code);
  cluster.fork();
});

参看child_process
event: ‘exit‘
.

Event: ‘setup‘

.setupMaster() 被调用的第一时间发送该事件。

cluster.setupMaster([settings])

  • settings {Object}

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

setupMaster 被用于更改默认的 fork 行为。一旦被调用,设置的内容将被存放于cluster.settings

注意:

  • 当且仅当首次调用 .setupMaster() 有效, 后续调用将被忽略。
  • 由于以上原因,一个工作进程的唯一属性可以被每个工作进程传递给.fork()的env环境变量参数所设置。
  • .fork() 内部调用.setupMaster()来创建默认值,
    副作用就是若要调用方法 .setupMaster()就必须在 .fork()之前。

示例:

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

这个函数只能在主进程中被调用。

cluster.fork([env])

  • env {Object} 添加到子进程环境变量中的键值对。
  • 返回 {Worker object}

派生一个新的工作进程。这个函数只能在主进程中被调用。

cluster.disconnect([callback])

  • 当callback {Function}被调用时,所有工作进程都将断开连接并且处理将被关闭。

每个在 cluster.workers的工作进程调用 .disconnect() 。

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

该方法带有一个可选的回调参数,会在完成时被调用。

这个函数只能在主进程中被调用。

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);
}

cluster.workers

  • Object

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

工作进程仅在 ‘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];
});

类: Worker

一个 Worker 对象包含了工作进程的所有公开信息和方法。可通过主进程中的 cluster.workers 或工作进程中的cluster.worker 取得。

worker.id

  • String

每个新的工作进程都被赋予一个唯一的标识,这个标识被储存在 id 中。

当一个工作进程可用时,这就是它被索引在
cluster.workers 中的主键。

worker.process

  • ChildProcess object

所有工作进程都是使用 child_process.fork() 创建的,该函数返回的对象被储存在
.process 属性中。在工作进程中则被存储在全局的process 变量中。

参看: Child
Process module

注意:只有 process  发生 ‘disconnect‘ 事件且 .suicide 属性不为true,则工作进程调用 process.exit(0) 来防止意外断开连接。

worker.suicide

  • Boolean

在通过 .kill() 或者 .disconnect() 设置之前值为 undefined

布尔值 worker.suicide 可以让你区分自主退出与意外退出,主进程将依据该值来决定是否重新派生一个工作进程。

cluster.on(‘exit‘, function(worker, code, signal) {
  if (worker.suicide === true) {
    console.log(‘Oh, it was just suicide\‘ – no need to worry‘).
  }
});

// kill worker
worker.kill();

worker.send(message, [sendHandle])

  • message Object
  • sendHandle Handle
    object

该函数等同于 child_process.fork() 提供的
send 方法。在主进程中您可以用该函数向特定工作进程发送消息。

在工作进程中您也能使用 process.send(message),因为它们是同一个函数。

这个示例会回应来自主进程的所有消息:

if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send(‘hi there‘);

} else if (cluster.isWorker) {
  process.on(‘message‘, function(msg) {
    process.send(msg);
  });
}

worker.kill([signal=‘SIGTERM‘])

  • signal {String} 发送给工作进程的终止信号的名称

这个函数将完结工作进程。在主进程下,通过断开 worker.process 的连接且仅通过 signal 来kill。在工作进程下,
通过断开通信通道且通过 0 来退出。

引起.suicide 的设置。

该方法的别名是 worker.destroy(),以保持向后兼容。

注意:在工作进程下, process.kill() 存在,这不是本函数而是 process.kill(pid,
[signal])。

worker.disconnect()

在工作进程下,该函数会关闭所有服务,并等待这些服务‘close‘事件的响应,然后断开IPC连接。

在主进程下,一个工作进程调用 .disconnect() 将产生一条发送给其本身的内部消息。

引起.suicide 的设置。

注意:调用该函数后工作进程将不再接受新连接,但新连接仍会被其它正在监听的工作进程处理。已存在的连接允许正常退出。当没有连接存在,连接到工作进程的 IPC 通道会被关闭,以便工作进程安全地结束。

以上仅要求服务连接和可会短连接不是被工作进程自主停止而且退出前disconnect ()不是在等待服务关闭。

注意:在工作进程下, process.disconnect 存在,但它不是本函数,而是child.disconnect()。

由于可能存在长生命周期服务断开连接阻塞其他工作进程,通常会发送一条消息告知应用来采取特定的动作来关闭服务。另一种做法是实现一个若一定时间内 disconnect 事件未发送就完结工作进程的超时机制。

if (cluster.isMaster) {
  var worker = cluster.fork();
  var timeout;

  worker.on(‘listening‘, function(address) {
    worker.send(‘shutdown‘);
    worker.disconnect();
    timeout = setTimeout(function() {
      worker.kill();
    }, 2000);
  });

  worker.on(‘disconnect‘, function() {
    clearTimeout(timeout);
  });

} else if (cluster.isWorker) {
  var net = require(‘net‘);
  var server = net.createServer(function(socket) {
    // connections never end
  });

  server.listen(8000);

  process.on(‘message‘, function(msg) {
    if(msg === ‘shutdown‘) {
      // initiate graceful close of any connections to server
    }
  });
}

Event: ‘message‘

  • message Object

该事件和 child_process.fork() 所提供的一样。

工作进程中您也可以使用process.on(‘message‘)

举个例子,这里有一个集群,使用消息系统在主进程中统计请求的数量:

var cluster = require(‘cluster‘);
var http = require(‘http‘);

if (cluster.isMaster) {

  // Keep track of http requests
  var numReqs = 0;
  setInterval(function() {
    console.log("numReqs =", numReqs);
  }, 1000);

  // Count requestes
  function messageHandler(msg) {
    if (msg.cmd && msg.cmd == ‘notifyRequest‘) {
      numReqs += 1;
    }
  }

  // Start workers and listen for messages containing notifyRequest
  var numCPUs = require(‘os‘).cpus().length;
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  Object.keys(cluster.workers).forEach(function(id) {
    cluster.workers[id].on(‘message‘, messageHandler);
  });

} else {

  // Worker processes have a http server.
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");

    // notify master about the request
    process.send({ cmd: ‘notifyRequest‘ });
  }).listen(8000);
}

Event: ‘online‘

和 cluster.on(‘online‘) 事件一样,但仅当特定工作进程的状态改变时发生。

cluster.fork().on(‘online‘, function() {
  // Worker is online
});

在当前工作进程内不发送该事件。

Event: ‘listening

  • address Object

和 cluster.on(‘listening‘) 事件一样,但仅当特定工作进程的状态改变时发生。

cluster.fork().on(‘listening‘, function(address) {
  // Worker is listening
});

在当前工作进程内不发送该事件。

Event: ‘disconnect‘

和 cluster.on(‘disconnect‘) 事件一样,但仅当特定工作进程的状态改变时发生。

cluster.fork().on(‘disconnect‘, function() {
  // Worker has disconnected
});

Event: ‘exit‘

  • code {Number} 如果是正常退出则为退出代码。
  • signal {String} 使得进程被终止的信号的名称(比如 ‘SIGHUP‘)。

由单个工作进程实例在底层子进程被结束时触发。详见子进程事件:
‘exit‘

var worker = cluster.fork();
worker.on(‘exit‘, function(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: ‘error‘

这个事件与 child_process.fork() 提供的事件相同。

在工作进程中也可以使用 process.on(‘error‘) 。

时间: 2024-10-16 12:25:01

Node.js v0.10.31API手册-集群的相关文章

Node.js v0.10.31API手册-Domain

Node.js v0.10.31API手册-目录 Domain(域) Domains 提供了一种方式,即以一个单一的组的形式来处理多个不同的IO操作.如果任何一个注册到domain的事件触发器或回调触发了一个'error'事件,或者抛出一个错误,那么domain对象将会被通知到.而不是直接让这个错误的上下文从`process.on('uncaughtException')'处理程序中丢失掉,也不会致使程序因为这个错误伴随着错误码立即退出. 警告: 不要忽视错误! Domain error处理程序

Node.js v0.10.31API手册-事件

Node.js v0.10.31API手册-目录 Events(事件) Node里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件. 所有这些产生事件的对象都是events.EventEmitter的实例. 你可以通过require("events")来访问该模块. 通常,事件名是驼峰命名 (camel-cased) 的字符串.不过也没有强制的要求,任何字符串都是可以使用的. 为了

Node.js v0.10.31API手册-DNS

原API变化,故译文与之前译文样式上有所不同 Node.js v0.10.31API手册-目录 DNS 使用 require('dns') 引入此模块.dns 模块中的所有方法都使用了 C-Ares,除了 dns.lookup 使用了线程池中的 getaddrinfo(3).C-Ares 比 getaddrinfo 要快得多,但系统解析器相对于其它程序的操作要更固定.当一个用户使用 net.connect(80, 'google.com') 或 http.get({ host: 'google.

Node.js v0.10.31API手册-目录

这段时间,想找些新的技术学习学习,看上了Node.js,万丈高楼平地起,这就从它的API开始,然后就顺便翻译一下. Node.js v0.10.31API手册-简介 Node.js v0.10.31API手册-断言 Node.js v0.10.31API手册-Buffer Node.js v0.10.31API手册-Addons插件 持续更新中...

Node.js v0.10.31API手册-加密

加密(Crypto) 使用 require('crypto') 来调用该模块. crypto模块提供在HTTPS或HTTP连接中封装安全凭证的方法. 它提供OpenSSL中的一系列哈希方法,包括hmac.cipher.decipher.签名和验证等方法的封装. crypto.getCiphers() 返回一个数组,包含支持的加密算法的名字. 示例: var ciphers = crypto.getCiphers(); console.log(ciphers); // ['AES-128-CBC'

Node.js v0.10.31API手册-子进程

子进程 Node 通过 child_process 模块提供了类似 popen(3) 的处理三向数据流(stdin/stdout/stderr)的功能. 它能够以完全非阻塞的方式与子进程的 stdin.stdout 和 stderr 以流式传递数据.(请注意,某些程序在内部使用行缓冲 I/O.这不会影响到 node.js,但您发送到子进程的数据不会被立即消费.) 使用 require('child_process').spawn()或者 require('child_process').fork

Node.js v0.10.31API手册-Addons插件

Addons Addons插件就是动态连接库.Addons插件将Node与C库和C++库链接起来.它的API(目前来说)相当复杂,涉及到了几个类库的知识. V8 JavaScript引擎是一个 C++ 类库..用于和JavaScript进行交互的接口.创建对象, 调用函数等. 文档大部分在这里:v8.h 头文件 (deps/v8/include/v8.h在Node源代码目录里), 也有可用的线上文档线上. libuv, C语言编写的事件循环类库.任何时候需要等待一个文件描述符变为可读状态,等待一

Node.js v0.10.31API手册-控制台

控制台 Object 用于向 stdout 和 stderr 打印字符.类似于大部分 Web 浏览器提供的 console 对象函数,在这里则是输出到 stdout 或 stderr. 当输出目标是一个终端或者文件时,console函数是同步的(为了防止过早退出时丢失信息).当输出目标是一个管道时它们是异步的(防止阻塞过长时间). 也就是说,在下面的例子中,stdout 是非阻塞的,而 stderr 则是阻塞的. $ node script.js 2> error.log | tee info.

Node.js v0.10.31API手册-Debugger

Debugger V8 提供了一个强大的调试器,可以通过 TCP 协议从外部访问.Node 内建了这个调试器的客户端.要使用调试器,以 debug 参数启动 Node,出现提示符: % node debug myscript.js < debugger listening on port 5858 connecting... ok break in /home/indutny/Code/git/indutny/myscript.js:1 1 x = 5; 2 setTimeout(functio