API
The Cluster API at its core is extremely simple, all we need to do is pass
our tcp or http server to cluster(), then call listen() as we would on the http.Server itself.
var cluster = require('../')
, http = require('http');
var server = http.createServer(function(req, res){
res.writeHead(200);
res.end('Hello World');
});
cluster(server)
.listen(3000);
Alternatively (and recommended) is to export your server instance via module.exports, and supply a path to cluster(). For example app.js:
module.exports = http.createServer(....);
and server.js with our cluster logic, allowing our server to be require()ed within tests, and preventing potential issues by having open database connections etc within the master processes, as only the workers need access to the server instance.
cluster('app')
.listen(3000);
A good example if this, is a long-lived database connection. Our app.js may have this initialized at the top, which although will work fine stand-alone, may cause cluster’s master processes to hang when restarting or closing due to the connection remaining active in the event loop.
var db = redis.createClient();
Plugins
A plugin simple a function that accepts the master process. Most plugin functions return another anonymous function, allowing them to accept options, for example:
function myPlugin(path){
return function(master) {
// do stuff
}
}
To use them, all we need to do is pass it to the use() method:
cluster(server)
.use(myPlugin('/some/path'))
.listen(3000);
To use a plugin that is bundled with Cluster simply grab it from the cluster object:
cluster(server)
.use(cluster.logger())
.listen(3000);
Settings
Below are the settings available:
-
workersNumber of workers to spawn, defaults to the number of CPUs or1 -
working directoryWorking directory defaulting to the script’s dir -
backlogConnection backlog, defaulting to 128 -
socket pathMaster socket path defaulting to./ -
timeoutWorker shutdown timeout in milliseconds, defaulting to60000 -
titlemaster process title defaulting to “cluster master” -
worker titleworker process title defaulting to “cluster worker {n}” -
userUser id / name -
groupGroup id / name
We can take what we have now, and go on to apply settings using the set(option, value) method. For example:
cluster(server)
.set('working directory', '/')
.set('workers', 5)
.listen(3000);
Signals
Cluster performs the following actions when handling signals:
-
SIGINThard shutdown -
SIGTERMhard shutdown -
SIGQUITgraceful shutdown -
SIGUSR2restart workers
Events
The following events are emitted, useful for plugins or general purpose logging etc.
-
start. When the server is starting (pre-spawn) -
worker. When a worker is spawned, passing theworker -
listening. When the server is listening for connections (post-spawn) -
closing. When master is gracefully shutting down -
close. When master has completed shutting down -
worker killed. When a worker has died -
worker exception. Worker uncaughtException. Receives the worker and exception object -
kill. When asignalis being sent to all workers -
restarting. Restart requested by REPL or signal. Receives an object which can be patched in order to preserve plugin state. -
restart. Restart complete, new master established, previous killed. Receives an object with state preserved by therestartingeven, patched in the previous master.
Master#state
Current state of the master process, one of:
-
active -
hard shutdown -
graceful shutdown
Master#isWorker
true when the script is executed as a worker.
cluster = cluster(server).listen(3000);
if (cluster.isWorker) {
// do something
}
Alternatively we can use the CLUSTER_WORKER env var, populated with the worker’s id.
Master#isMaster
true when the script is executed as master.
cluster = cluster(server).listen(3000);
if (cluster.isMaster) {
// do something
}
Master#set(option, value)
Set option to value.
Master#use(plugin)
Register a plugin for use.
Master#in(env)
Conditionally perform the following action, if
NODE_ENV matches env.
cluster(server)
.in('development').use(cluster.debug())
.in('development').listen(3000)
.in('production').listen(80);
The environment conditionals may be applied to several calls:
cluster(server)
.set('working directory', '/')
.in('development')
.set('workers', 1)
.use(cluster.logger('logs', 'debug'))
.use(cluster.debug())
.listen(3000)
.in('production')
.set('workers', 4)
.use(cluster.logger())
.use(cluster.pidfiles())
.listen(80);
If we perform the same action for environments, set them before
the first in() call, or use in('all').
cluster(server)
.set('working directory', '/')
.do(function(){
console.log('some arbitrary action');
})
.in('development')
.set('workers', 1)
.use(cluster.logger('logs', 'debug'))
.use(cluster.debug())
.in('production')
.set('workers', 4)
.use(cluster.logger())
.use(cluster.pidfiles())
.in('all')
.listen(80);
Master#spawn(n)
Spawn n additional workers.
Master#close()
Graceful shutdown, waits for all workers to reply before exiting.
Master#destroy()
Hard shutdown, immediately kill all workers.
Master#restart([signal])
Defaults to a graceful restart, spawning a new master process, and sending SIGQUIT to the previous master process. Alternatively a custom signal may be passed.
Master#kill([signal])
Sends SIGTERM or signal to all worker processes. This method is used by Master#restart(), Master#close() etc.