api.md 6.5 KB

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

Abstract Clusters

Cluster is not bound to servers, cluster can be used to manage processes for processing job queues etc. Below is a minimalist example of this, simply invokes cluster() with no object, spawning a worker per cpu:

  var cluster = require('cluster');

  var proc = cluster().start();

  if (proc.isWorker) {
    // do things within the worker processes
  } else {
    // do something within the master
  }

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:

  • workers Number of workers to spawn, defaults to the number of CPUs or 1
  • working directory Working directory defaulting to the script's dir
  • backlog Connection backlog, defaulting to 128
  • socket port Master socket port defaulting to 8989
  • timeout Worker shutdown timeout in milliseconds, defaulting to 60000
  • title master process title defaulting to "cluster"
  • worker title worker process title defaulting to "cluster worker"
  • user User id / name
  • group Group 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:

  • SIGINT hard shutdown
  • SIGTERM hard shutdown
  • SIGQUIT graceful shutdown
  • SIGUSR2 restart workers

Events

The following events are emitted, useful for plugins or general purpose logging etc.

  • start. When the IPC server is prepped
  • worker. When a worker is spawned, passing the worker
  • listening. When the server is listening for connections
  • closing. When master is 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 a signal is 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 the restarting even, 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.