whipper v0.1.0
whipper
A child process management library for node.js
What Whipper Does
Whipper lets you delegate work to a managed pool of worker child processes. It gives you a rich, streamlined API for manipulating worker pools and individual workers, and provides the fault tolerance so you can focus on your business logic.
A whipper worker is an abstraction on top of child processes in that it may have many during its lifetime, but only one at a time. If configured to do so, a worker will re-fork the underlying process if it was killed externally, or by error, or any other means. It can process one call at a time, or however many you specify in parallel.
A whipper pool manages a bunch of these workers, doing things like distributing the work load according to a configurable strategy, and managing when workers are created and destroyed.
Install
$ npm install --save whipper
Usage
First, define a worker module:
// my-worker.js
function hi(person) {
return Array(400).join('hi ' + person);
}
// Expose the interface by which whipper will call me
module.exports = {
sayHi: function(person) {
// Reply now
return hi(person);
},
sayHiLater: function(person, done) {
// Reply later
process.nextTick(function() {
done(hi(person));
});
},
promiseToSayHi: function(person) {
// es6
return new Promise(function(resolve, reject) {
process.nextTick(function() {
resolve(hi(person));
});
});
}
};
All of these worker methods would behave identically, but you can select whichever reply style is best for your worker design.
Create a whipper pool with basic configuration:
// app.js
var Whipper = require('whipper');
var whipper = new Whipper({
// Required: The path to your worker module
workerModulePath: require.resolve('./my-worker'),
// The minimum number of child processes to be kept alive at all times
minWorkers: 2,
// The maximum number of child processes
maxWorkers: 16,
// The maximum number of calls a single worker will handle at a time
maxConcurrentCalls: 4,
// Timeout if worker calls take longer than...
invocationTimeout: 60000,
// Specify what to do with a call when all workers are busy
atCapacityStrategy: "queue", // or "drop", "error"
// Access logging output
logger: function(level, message) {
console.log("whipper: ["+ level + "] " + message);
}
// More docs. Coming soon.
});
Listen to what's going on:
whipper
.on("worker:pool:available", function() {
console.log("Ah yeah, some workers are available");
})
.on("worker:pool:unavailable", function() {
console.log("Shit! All the workers are busy!");
})
.on("worker:process:created", function(worker) {
console.log("Get to work!", worker.pid());
})
.on("worker:process:exited", function() {
console.log("You're fired!", worker.pid());
})
.on("worker:state:changed", function(worker) {
console.log("Worker now in state: ", worker.state());
/* Will print one of these states:
created
forking
processing
flushing
dying
destroying
*/
});
Invoke a worker function:
whipper.invoke("sayHi", "Winfred").then(function(result) {
console.log(result);
}).fail(function(err) {
console.log("Fine. Be that way.", err.message);
})
The output of this call would normally be: "hi Winfred hi Winfred hi Winfred..."
(times a bajillion)
Or, create a proxy to the worker interface:
whipper.workerProxy().then(function(worker) {
worker.sayHi("Ted").then(function(result) {
console.log("Worker said:", result);
});
});
Get a bunch of information out of whipper:
console.log(whipper.stats());
... which would output something like:
{
atCapacityWorkerCount: 2,
busyWorkerCount: 4,
idleWorkerCount: 6,
maxSeenWorkerCount: 20,
workerCount: 10,
workers: {
callsHandled: 446,
errorCount: 3,
forkCount: 20,
maxSeenConcurrentCalls: 6,
resetCount: 0
}
}
Road Map
- Way more documentation
- Published jsdoc
- Published test coverage reports
- Synchronous proxy creation
- Support for passing socket objects, like the standard child api
Versioning
While in the 0.x.x range of versions, which denote alpha status, releases may introduce backwards incompatible changes. Following a 1.x.x release, standard semantic versioning will apply regarding public api-breaking changes.
License
MIT © Troy Kinsella