Home

Awesome

cluster-service

Build Status NPM version Dependency Status Bitdeli Badge

NPM NPM

Install

npm install cluster-service

https://npmjs.org/package/cluster-service

About

Turn your single process code into a fault-resilient, multi-process service with
built-in REST & CLI support. Restart or hot upgrade your web servers with zero
downtime or impact to clients.

Presentation:

http://x.co/bpnode

Video:

http://x.co/bpnodevid

Getting Started

Your existing application, be it console app or service of some kind:

// server.js
console.log("Hello World");

Leveraging cluster-service without adding a line of code:

npm install -g cluster-service
cservice "server.js" --accessKey "lksjdf982734"
// cserviced "server.js" --accessKey "lksjdf982734" // daemon

This can be done without a global install as well, by updating your package.json:

"scripts": {
	"start": "cservice server.js --accessKey lksjdf982734"
},
"dependencies": {
	"cluster-service": ">=0.5.0"
}

Now we can leverage npm to find our local install of cluster-service:

npm start

Or, if you prefer to control cluster-service within your code, we've got you covered:

// server.js
require("cluster-service").start({ workers: "./worker.js", accessKey: "lksjdf982734" });

// worker.js
console.log("Hello World"); // notice we moved our original app logic to the worker

Talk to it

Now that your service is resilient to worker failure, and utilizing all cores of your machine, lets talk to it. With your service running, type into the command-line:

restart all

or for a full list of commands...

help

or for help on a specific command:

help {command}

We can also issue commands from a seperate process, or even a remote machine (assuming proper access):

npm install -g cluster-service
cservice "restart all" --accessKey "my_access_key"

You can even pipe raw JSON for processing:

cservice "restart all" --accessKey "my_access_key" --json

Check out Cluster Commands for more.

Start Options

When initializing your service, you have a number of options available:

cservice "server.js" --accessKey "123"

Or via JSON config:

cservice "config.json"

Or within your node app:

// server.js
// inline options
require("cluster-service").start({ workers: "worker.js", accessKey: "123" });
// or via config
require("cluster-service").start({ config: "config.json" });

Options:

Console & REST API

A DPS Cluster Service has two interfaces, the console (stdio), and an HTTP REST API. The two interfaces are treated identical, as console input/output is piped over the REST API. The reason for the piping is that a DPS Cluster Service is intentionally designed to only support one instance of the given service running at any one time, and the port binding is the resource constraint. This allows secondary services to act as console-only interfaces as they pipe all input/output over HTTP to the already running service that owns the port. This flow enables the CLI to background processes. The REST API is locked to a "accessKey" expected in the query string. The console automatically passes this key to the REST API, but for external REST API access, the key will need to be known.

{ host: "localhost", port: 11987, accessKey: "lksjdf982734" }

Invoking the REST interface directly would look something like:

curl -d "" "http://localhost:11987/cli?cmd=help&accessKey=lksjdf982734"

Or better yet, use the run option to do the work for you:

cservice "help" --accessKey "lksjdf982734"
// same as
cservice --run "help" --accessKey "lksjdf982734"

Cluster Commands

While a Cluster Service may provide its own custom commands, below are provided out-of-the-box. Commands may be disabled by overriding them.

Commands & Events

Creating custom, or overriding commands and events is as simple as:

cservice "server.js" --commands "./commands,../some_more_commands"

Or if you prefer to manually do so via code:

var cservice = require("cluster-service");
cservice.on("custom", function(evt, cb, arg1, arg2) { // "custom" command
	// can also fire custom events
	cservice.trigger("on.custom.complete", 1, 2, 3);
};

cservice.on("test", function(evt, cb, testScript, timeout) { // we're overriding the "test" command
	// arguments
	// do something, no callback required (events may optionally be triggered)
};

// can also issue commands programatically
cservice.trigger("custom", function(err) { /* my callback */ }, "arg1value", "arg2value");

Cluster Events

Events are emitted to interested parties.

Async Support

While web servers are automatically wired up and do not require async logic (as of v1.0), if your service requires any other asynchronous initialization code before being ready, this is how it can be done.

Have the worker inform the master once it is actually ready:

// worker.js
require("cluster-service").workerReady(false); // we're NOT ready!
setTimeout(funtion() {
	// dumb example of async support
	require("cluster-service").workerReady(); // we're ready!
}, 1000);

Additionally, a worker may optionally perform cleanup tasks prior to exit, via:

// worker.js
require("cluster-service").workerReady({
	onWorkerStop: function() {
		// lets clean this place up
		process.exit(); // we're responsible for exiting if we register this cb
	}
});

Access Control

Commands may be granted "inproc" (high risk), "local" (low risk), or "remote" (no risk). Setting access control can be done within the command, like so:

// exit.js
module.exports.control = function(){
	return "local";
};

Or may be overriden at runtime via:

// server.js
require("cluster-service").control({ "exit": "local" });

Proxy Support

Proxy mode specifically caters to Web Servers that you want to enable automatic versioning of your service. Any version requested (via versionHeader) that is not yet loaded will automatically have a worker process spun up with the new version, and after ready, the proxy will route to that worker.

Every version of your app must adhere to the PROXY_PORT environment variable like so:

require("http").createServer(function(req, res) {
  res.writeHead(200);
  res.end("Hello world!");
}).listen(process.env.PROXY_PORT || 3000 /* port to use when not running in proxy mode */);

Proxy Options

Proxy Bindings

Binding options:

A full list of TLS Options: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener

Proxy Commands

Work like any other Cluster Commands.

Tests & Code Coverage

Download and install:

git clone https://github.com/godaddy/node-cluster-service.git
cd node-cluster-service
npm install

Now test:

npm test

View code coverage in any browser:

coverage/lcov-report/index.html

Change Log

Change Log

License

MIT