[size=large][b]Cluster 集群[/b][/size]
一个独立的Node对象运行在单个线程中。为了更好的利用多核心系统的,用户会尝试启动多个Node处理进程,提供更好的服务。(说明:单个线程总是运行在一个核心上面,cluster是为了更好的利用CPU,充分利用多核CPU的资源)
你可以使用cluster模块,非常容易的创建共享服务端口的多个网络处理应用程序。
(在多个进程中,需要考虑全局变量等的存储及访问)
[quote]% node server.js
Worker 2438 online
Worker 2437 online [/quote]
这个功能是近期推出的,在后续的版本中可能会继续更改。请尝试并进行反馈。
注意:在windows中,现在还不能在一个worker中启动一个命名管道
[b]它如何工作[/b]
worker 进程的创建是通过child_process.fork方法启动的,所以它可以通过IPC和父进程进行双向传递信息。
当你在一个worker中调用server.listen(...)的时候,它将参数序列化,并传递到主进程(master process)中。如果主进程已经有满足workder需求的监听server,则传递handle到这个worker上。如果不存在满足需求的监听server,则主进程创建一个然后将handle传递给子进程。
在边界条件,这将导致三个令人惊讶的情况:
1.server.listen({fd:7}) 因为消息是传递到master中的,父进程应该监听“文件描述为7”,然后传递到worker进行处理,而不是将“文件描述为7”的引入传递到worker中
2.server.listen(handle) 监听明确的处理函数将导致worker使用提供的handle函数处理,而不是与父进程联系,如果worker已经含有了handle,它假设你已经知道你将要做什么。
3.server.listen(0) 通常俯冲下,这会使服务监听一个随机的的端口,但是在cluster中,每一个worker在linten(0)的时候每次将接收同样的随机端口的信息,本质上来说,这个端口在一开始的时候是随机的,并且在在可预见的未来。如果你想监听一个指定的端口,基于cluster worker ID生成一个
当多个进程都在 accept()ing 同样的资源的时候,操作系统的负载均衡非常高效。Node.js没有路由逻辑,个人程序逻辑中也没有,两个worker之前没有共享状态。所以,个人程序,必需被设计得不那么重,比如基于内存的session或者登录相关的数据(这些可以被设计成存储于独立的缓存中,如memcached或者是redis中)。
因为workers都是独力运行的,根据程序的需要,它们可以被独立删除或者重启,它并不影响其他的worker。
只要还存在workers存活,则server将继续接收连接。Node不自动维护workers的数目。为应用程序维护worker池是你的责任。(可以在这里写一下监控,保证服务的持续运行)
[b]cluster.settings[/b]
Object 类型
[list]
[*]exec: [String] 传递给worker的文件路径。(默认=__filename)
[*]args: [Array] 传递给worker的数组参数。(默认=process.argv.slice(2))
[*]silent: [Boolean] 是否将输出传递到父进程的标准io。(默认=false)
[/list]
由.setupMaster存储的settings存储在当前这个settings对象中。这个对象不建议被修改。
[b]cluster.isMaster[/b]
Boolean类型
如果进程是master则返回true。这是由process.env.NODE_UNIQUE_ID决定的。如果
process.env.NODE_UNIQUE_ID是undefined,isMaster为true
[b]cluster.isWorker[/b]
Boolean类型
如果当前进程是一个从master进程fork出来的,则标志位为true。如果process.env.NODE_UNIQUE_ID被设置为一个值,isWorker为true
[b]Event: 'fork'[/b]
参数:worker [Worker object]
一个新的worker被cluster模块fork出来,触发一个'fork'事件。这可以被用于记录worker激活,并创建一个时间函数
[b] Event: 'online'[/b]
参数:worker [Worker object]
forking一个新的worker之后,此worker应该响应在线的消息。当master接收到此在线消息后,它将触发此事件。'fork'和'online'事件的不同点在于,fork是在master创建新worker的时候触发,而online是在worker被执行的时候触发。
[b] Event: 'listening'[/b]
参数:worker [Workder object], address [Object]
worker调用listen()时,'listening'事件被自动发送到服务server对象中。当server正在监听一个消息,并发送到master进程的时候触发'listening' 事件
[b] Event: 'disconnect'[/b]
参数:worker [Workder object]
当然一个worker的IPC通道失去连接之后触发'disconnect'事件。当worker死掉后发生,经常是在调用.destroy()方法后。
当调用.disconnect(),有可能在disconnect和exit事件之前延迟。此事件经常被用于确认进程是否卡在清理或者长连接中。
[b] Event: 'exit'[/b]
参数:
worker [Worker object]
code [Number] 退出码,如果正常退出的话
signal [String] 导致进程退出的信号的名称
worker进程挂掉后cluster模块触发'exit'事件。可以通过调用fork()重新启动该worker
[b] Event: 'setup'[/b]
参数:worker [Worker object]
调用.setupMaster()触发此事件。如果.setupMaster()在fork()函数前没有执行,则此函数会调用没有参数的.setupMaster()。
[b]cluster.setupMaster([setting])[/b]
参数:settings Object
[list]
[*]exec: [String] 传递给worker的文件路径。(默认=__filename)
[*]args: [Array] 传递给worker的数组参数。(默认=process.argv.slice(2))
[*]silent: [Boolean] 是否将输出传递到父进程的标准io。(默认=false)
[/list]
setupMaster 用于改变默认的'fork'行为。新的settings会立即生效,并且是永远的,不可修改的。
[b]cluster.fork([env])[/b]
[list]
[*]env [Object] 加入到子进程环境的Key/Value对
[*]return [Worker object]
[/list]
启动一个新的worker进程。只能由主进程调用。(在调用之前判断cluster.isMaster)
[b]cluster.disconnect([callback])[/b]
调用此方法的时候,所有的worker将提交一个优雅的结束。当所有的worker都失去全局的连接,并且全局的控件也关闭,没有事件等待,此时主进程(master process)将优雅的结束
结束的时候,这个方法接收一个可配置的callback参数。
[b]cluster.worker[/b]
Object类型
对当前worker对象的引用。在主进程程不可用。
[b]cluster.workers[/b]
Object类型
存储所有workds对象的hash,通过id作为key来标志。非常使得的循环所有的worker。只在主进程中可用。
可能你会通过一个交流频道来与worker的引用交流,使用worker的唯一ID可以很快找出worker
[b]Class: Worker[/b]
包仿worker所有公共信息和方法的Worker对象。在主进程中可以通过cluster.workers获得。在worker中,可能通过cluster.worker获得
[b]work.id[/b]
String类型
每一个Worker提供独立的id,存储在id属性中
当一个worker存活着的时候,这个key标识在cluster.workers中
[b]worker.process[/b]
ChildProcess Object 类型
所有的workers都是通过child_process.fork()创建的,它的返回值被存储在process属性中
[b]worker.suicide[/b]
Boolean类型
.destroy()后worker挂掉,或者在调用.disconnect()方法后,将设置此属性。否则它的值是undefined
[b]worker.send(message, [sendHandle])[/b]
参数:message [Object]
sendHandle [Handle object]
这个方法等同于child_process.fork()。在主进程中,可以使用此方法发送一个消息到指定的worker中。在worker中,一样可以发送一个消息到主进种中,完成同样的功能。
[b] worker.destroy()[/b]
这个方法杀掉worker,然后通知主进行不再启动一个新的worker。Boolean值suicide让你区分出是正常,或者意外退出。
[b] worker.disconnect()[/b]
当调用此方法的时候,worker不再接收新的连接请求,但它被其他监听着的worker控制。已存在的连接将会允许正常情的退出。已没有连接存在的情况下,worker的IPC频道将关闭,允许worker优雅的结束。IPC频道关闭时触发disconnect事件,跟着是exit事件,在worker完全退出的时候。
也许有长的存活的连接存在,最好是实现一个timeout函数。示例里,呼叫worker断开连接,并在2秒后结束server。一个可供选择的去执行worker.destroy()在两提前后,但它不能使用worker去正常的执行一些必需的清理工作。
[b] Event: 'message'[/b]
参数:message [Object]
这个事件与child_process.fork()提供的相同。在主进程中,可以使用这个事件,在worker中,可以使用process.on('message')
示例,主进程使用消息系统发送多个requrest:
[b]Event: 'online'[/b]
与cluster.on('online')相同的事件,指定的worker发生状态改变时触发。
[b] Event: 'listening'[/b]
参数:address Object
与cluster.on('listening')相同的事件,指定的worker发生状态改变时触发。
[b] Event: 'disconnect'[/b]
与cluster.on('disconnect')相同的事件,指定的worker发生状态改变时触发。
[b] Event: 'exit'[/b]
[list]
[*]code [Number] 退出码,如果正常退出的话
[*]signal [String] 导致进程退出的信号的名称
[/list]
在个另的worker对象中触发,特别是子进程结束的时候。可以参考:child_process event: 'exit'
一个独立的Node对象运行在单个线程中。为了更好的利用多核心系统的,用户会尝试启动多个Node处理进程,提供更好的服务。(说明:单个线程总是运行在一个核心上面,cluster是为了更好的利用CPU,充分利用多核CPU的资源)
你可以使用cluster模块,非常容易的创建共享服务端口的多个网络处理应用程序。
(在多个进程中,需要考虑全局变量等的存储及访问)
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++) {
var worker = cluster.fork();
consolog.log('Worker ' + worker.process.pid + ' online');
}
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);
}
[quote]% node server.js
Worker 2438 online
Worker 2437 online [/quote]
这个功能是近期推出的,在后续的版本中可能会继续更改。请尝试并进行反馈。
注意:在windows中,现在还不能在一个worker中启动一个命名管道
[b]它如何工作[/b]
worker 进程的创建是通过child_process.fork方法启动的,所以它可以通过IPC和父进程进行双向传递信息。
当你在一个worker中调用server.listen(...)的时候,它将参数序列化,并传递到主进程(master process)中。如果主进程已经有满足workder需求的监听server,则传递handle到这个worker上。如果不存在满足需求的监听server,则主进程创建一个然后将handle传递给子进程。
在边界条件,这将导致三个令人惊讶的情况:
1.server.listen({fd:7}) 因为消息是传递到master中的,父进程应该监听“文件描述为7”,然后传递到worker进行处理,而不是将“文件描述为7”的引入传递到worker中
2.server.listen(handle) 监听明确的处理函数将导致worker使用提供的handle函数处理,而不是与父进程联系,如果worker已经含有了handle,它假设你已经知道你将要做什么。
3.server.listen(0) 通常俯冲下,这会使服务监听一个随机的的端口,但是在cluster中,每一个worker在linten(0)的时候每次将接收同样的随机端口的信息,本质上来说,这个端口在一开始的时候是随机的,并且在在可预见的未来。如果你想监听一个指定的端口,基于cluster worker ID生成一个
当多个进程都在 accept()ing 同样的资源的时候,操作系统的负载均衡非常高效。Node.js没有路由逻辑,个人程序逻辑中也没有,两个worker之前没有共享状态。所以,个人程序,必需被设计得不那么重,比如基于内存的session或者登录相关的数据(这些可以被设计成存储于独立的缓存中,如memcached或者是redis中)。
因为workers都是独力运行的,根据程序的需要,它们可以被独立删除或者重启,它并不影响其他的worker。
只要还存在workers存活,则server将继续接收连接。Node不自动维护workers的数目。为应用程序维护worker池是你的责任。(可以在这里写一下监控,保证服务的持续运行)
[b]cluster.settings[/b]
Object 类型
[list]
[*]exec: [String] 传递给worker的文件路径。(默认=__filename)
[*]args: [Array] 传递给worker的数组参数。(默认=process.argv.slice(2))
[*]silent: [Boolean] 是否将输出传递到父进程的标准io。(默认=false)
[/list]
由.setupMaster存储的settings存储在当前这个settings对象中。这个对象不建议被修改。
[b]cluster.isMaster[/b]
Boolean类型
如果进程是master则返回true。这是由process.env.NODE_UNIQUE_ID决定的。如果
process.env.NODE_UNIQUE_ID是undefined,isMaster为true
[b]cluster.isWorker[/b]
Boolean类型
如果当前进程是一个从master进程fork出来的,则标志位为true。如果process.env.NODE_UNIQUE_ID被设置为一个值,isWorker为true
[b]Event: 'fork'[/b]
参数:worker [Worker object]
一个新的worker被cluster模块fork出来,触发一个'fork'事件。这可以被用于记录worker激活,并创建一个时间函数
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();
});
[b] Event: 'online'[/b]
参数:worker [Worker object]
forking一个新的worker之后,此worker应该响应在线的消息。当master接收到此在线消息后,它将触发此事件。'fork'和'online'事件的不同点在于,fork是在master创建新worker的时候触发,而online是在worker被执行的时候触发。
cluster.on('online', function(worker) {
console.log("Yay, the worker responded after it was forked");
});
[b] Event: 'listening'[/b]
参数:worker [Workder object], address [Object]
worker调用listen()时,'listening'事件被自动发送到服务server对象中。当server正在监听一个消息,并发送到master进程的时候触发'listening' 事件
cluster.on('listening', function(worker, address) {
console.log("A worker is now connected to " + address.address + ":"; + address.port);
});
[b] Event: 'disconnect'[/b]
参数:worker [Workder object]
当然一个worker的IPC通道失去连接之后触发'disconnect'事件。当worker死掉后发生,经常是在调用.destroy()方法后。
当调用.disconnect(),有可能在disconnect和exit事件之前延迟。此事件经常被用于确认进程是否卡在清理或者长连接中。
cluster.on('disconnect', function(worker) {
console.log('The worker #' + worker.id + ' has disconnected');
});
[b] Event: 'exit'[/b]
参数:
worker [Worker object]
code [Number] 退出码,如果正常退出的话
signal [String] 导致进程退出的信号的名称
worker进程挂掉后cluster模块触发'exit'事件。可以通过调用fork()重新启动该worker
cluster.on('exit', function(worker, code, signal) {
var exitCode = worker.process.exitCode;
console.log('worker ' + worker.process.pid + ' died ('+exitCode+'). restarting...');
cluster.fork();
});
[b] Event: 'setup'[/b]
参数:worker [Worker object]
调用.setupMaster()触发此事件。如果.setupMaster()在fork()函数前没有执行,则此函数会调用没有参数的.setupMaster()。
[b]cluster.setupMaster([setting])[/b]
参数:settings Object
[list]
[*]exec: [String] 传递给worker的文件路径。(默认=__filename)
[*]args: [Array] 传递给worker的数组参数。(默认=process.argv.slice(2))
[*]silent: [Boolean] 是否将输出传递到父进程的标准io。(默认=false)
[/list]
setupMaster 用于改变默认的'fork'行为。新的settings会立即生效,并且是永远的,不可修改的。
var cluster = require("cluster");
cluster.setupMaster({
exec : "worker.js",
args : ["--use", "https"],
silent : true
});
cluster.fork();
[b]cluster.fork([env])[/b]
[list]
[*]env [Object] 加入到子进程环境的Key/Value对
[*]return [Worker object]
[/list]
启动一个新的worker进程。只能由主进程调用。(在调用之前判断cluster.isMaster)
[b]cluster.disconnect([callback])[/b]
调用此方法的时候,所有的worker将提交一个优雅的结束。当所有的worker都失去全局的连接,并且全局的控件也关闭,没有事件等待,此时主进程(master process)将优雅的结束
结束的时候,这个方法接收一个可配置的callback参数。
[b]cluster.worker[/b]
Object类型
对当前worker对象的引用。在主进程程不可用。
var cluster = require('cluster');
if (cluster.isMaster) {
console.log('I am master');
cluster.fork();
cluster.fork();
} else if (cluster.isWorker) {
console.log('I am worker #' + cluster.worker.id);
}
[b]cluster.workers[/b]
Object类型
存储所有workds对象的hash,通过id作为key来标志。非常使得的循环所有的worker。只在主进程中可用。
// Go through all workers
function eachWorker(callback) {
for (var id in cluster.workers) {
callback(cluster.workers[id]);
}
}
eachWorker(function(worker) {
worker.send('big announcement to all workers');
});
可能你会通过一个交流频道来与worker的引用交流,使用worker的唯一ID可以很快找出worker
socket.on('data', function(id) {
var worker = cluster.workers[id];
});
[b]Class: Worker[/b]
包仿worker所有公共信息和方法的Worker对象。在主进程中可以通过cluster.workers获得。在worker中,可能通过cluster.worker获得
[b]work.id[/b]
String类型
每一个Worker提供独立的id,存储在id属性中
当一个worker存活着的时候,这个key标识在cluster.workers中
[b]worker.process[/b]
ChildProcess Object 类型
所有的workers都是通过child_process.fork()创建的,它的返回值被存储在process属性中
[b]worker.suicide[/b]
Boolean类型
.destroy()后worker挂掉,或者在调用.disconnect()方法后,将设置此属性。否则它的值是undefined
[b]worker.send(message, [sendHandle])[/b]
参数:message [Object]
sendHandle [Handle object]
这个方法等同于child_process.fork()。在主进程中,可以使用此方法发送一个消息到指定的worker中。在worker中,一样可以发送一个消息到主进种中,完成同样的功能。
if (cluster.isMaster) {
var worker = cluster.fork();
worker.send('hi there');
} else if (cluster.isWorker) {
process.on('message', function(msg) {
process.send(msg);
});
}
[b] worker.destroy()[/b]
这个方法杀掉worker,然后通知主进行不再启动一个新的worker。Boolean值suicide让你区分出是正常,或者意外退出。
cluster.on('exit', function(worker, code, signal) {
if (worker.suicide === true) {
console.log('Oh, it was just suicide\' – no need to worry').
}
});
// destroy worker
worker.destroy();
[b] worker.disconnect()[/b]
当调用此方法的时候,worker不再接收新的连接请求,但它被其他监听着的worker控制。已存在的连接将会允许正常情的退出。已没有连接存在的情况下,worker的IPC频道将关闭,允许worker优雅的结束。IPC频道关闭时触发disconnect事件,跟着是exit事件,在worker完全退出的时候。
也许有长的存活的连接存在,最好是实现一个timeout函数。示例里,呼叫worker断开连接,并在2秒后结束server。一个可供选择的去执行worker.destroy()在两提前后,但它不能使用worker去正常的执行一些必需的清理工作。
if (cluster.isMaster) {
var worker = cluster.fork();
var timeout;
worker.on('listening', function(address) {
worker.disconnect();
timeout = setTimeout(function() {
worker.send('force kill');
}, 2000);
});
worker.on('disconnect', function() {
clearTimeout(timeout);
});
} else if (cluster.isWorker) {
var net = require('net');
var server = net.createServer(function(socket) {
// connection never end
});
server.listen(8000);
server.on('close', function() {
// cleanup
});
process.on('message', function(msg) {
if (msg === 'force kill') {
server.destroy();
}
});
}
[b] Event: 'message'[/b]
参数:message [Object]
这个事件与child_process.fork()提供的相同。在主进程中,可以使用这个事件,在worker中,可以使用process.on('message')
示例,主进程使用消息系统发送多个requrest:
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);
}
[b]Event: 'online'[/b]
与cluster.on('online')相同的事件,指定的worker发生状态改变时触发。
cluster.fork().on('online', function() {
// Worker is online
};
[b] Event: 'listening'[/b]
参数:address Object
与cluster.on('listening')相同的事件,指定的worker发生状态改变时触发。
cluster.fork().on('listening', function(address) {
// Worker is listening
};
[b] Event: 'disconnect'[/b]
与cluster.on('disconnect')相同的事件,指定的worker发生状态改变时触发。
cluster.fork().on('disconnect', function(address) {
// Worker is listening
};
[b] Event: 'exit'[/b]
[list]
[*]code [Number] 退出码,如果正常退出的话
[*]signal [String] 导致进程退出的信号的名称
[/list]
在个另的worker对象中触发,特别是子进程结束的时候。可以参考:child_process event: '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!");
}
};