version app + node.exe + nw.exe
This commit is contained in:
54
node_modules/cluster/lib/cluster.js
generated
vendored
Normal file
54
node_modules/cluster/lib/cluster.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
/*!
|
||||
* Cluster
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Master = require('./master')
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Export `start` as the module.
|
||||
*/
|
||||
|
||||
exports = module.exports = start;
|
||||
|
||||
/**
|
||||
* Library version.
|
||||
*/
|
||||
|
||||
exports.version = '0.7.7';
|
||||
|
||||
/**
|
||||
* Expose utils.
|
||||
*/
|
||||
|
||||
exports.utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Start a new `Master` with the given `server`.
|
||||
*
|
||||
* @param {http.Server} server
|
||||
* @return {Master}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function start(server) {
|
||||
return new Master(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose middleware via lazy-requires.
|
||||
*/
|
||||
|
||||
fs.readdirSync(__dirname + '/plugins').forEach(function(plugin){
|
||||
plugin = plugin.replace('.js', '');
|
||||
exports.__defineGetter__(plugin, function(){
|
||||
return require('./plugins/' + plugin);
|
||||
});
|
||||
});
|
||||
916
node_modules/cluster/lib/master.js
generated
vendored
Normal file
916
node_modules/cluster/lib/master.js
generated
vendored
Normal file
@@ -0,0 +1,916 @@
|
||||
|
||||
/*!
|
||||
* Cluster - Master
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Worker = require('./worker')
|
||||
, EventEmitter = require('events').EventEmitter
|
||||
, dirname = require('path').dirname
|
||||
, spawn = require('child_process').spawn
|
||||
, utils = require('./utils')
|
||||
, fsBinding = process.binding('fs')
|
||||
, netBinding = process.binding('net')
|
||||
, bind = netBinding.bind
|
||||
, listen = netBinding.listen
|
||||
, socket = netBinding.socket
|
||||
, socketpair = netBinding.socketpair
|
||||
, close = netBinding.close
|
||||
, unlink = fsBinding.unlink
|
||||
, dgram = require('dgram')
|
||||
, tty = require('tty')
|
||||
, net = require('net')
|
||||
, fs = require('fs')
|
||||
, os = require('os');
|
||||
|
||||
/**
|
||||
* Node binary.
|
||||
*/
|
||||
|
||||
var node = process.execPath;
|
||||
|
||||
/**
|
||||
* Start a new `Master` with the given `server` or filename to
|
||||
* a node module exporting a server.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `workers` Number of workers to spawn, defaults to the number of CPUs
|
||||
* - '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 60,000
|
||||
* - 'user` User id / name
|
||||
* - 'group` Group id / name
|
||||
* - `title` Master process title, defaults to "cluster master"
|
||||
* - `worker title` Worker process title, defaults to "cluster worker {n}"
|
||||
*
|
||||
* Events:
|
||||
*
|
||||
* - `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 / exception
|
||||
* - `worker removed`. Worker removed via `spawn(-n)`
|
||||
* - `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 died.
|
||||
* Receives an object with state preserved by the `restarting` event.
|
||||
*
|
||||
* Signals:
|
||||
*
|
||||
* - `SIGINT` hard shutdown
|
||||
* - `SIGTERM` hard shutdown
|
||||
* - `SIGQUIT` graceful shutdown
|
||||
* - `SIGUSR2` graceful restart
|
||||
*
|
||||
* @param {net.Server|String} server
|
||||
* @return {Master}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var Master = module.exports = function Master(server) {
|
||||
var self = this;
|
||||
this.server = server;
|
||||
this.plugins = [];
|
||||
this.children = [];
|
||||
this.state = 'active';
|
||||
this.startup = new Date;
|
||||
this._killed = 0;
|
||||
|
||||
// grab server root
|
||||
this.cmd = process.argv.slice(1);
|
||||
this.dir = dirname(this.cmd[0]);
|
||||
|
||||
// environment
|
||||
this.env = process.env.NODE_ENV || 'development';
|
||||
|
||||
// defaults
|
||||
this.options = {
|
||||
'backlog': 128
|
||||
, 'working directory': this.dir
|
||||
, 'socket port': 8989
|
||||
, 'socket addr': '127.0.0.1'
|
||||
, 'timeout': 60000
|
||||
, 'restart threshold': 'development' == this.env ? 5000 : 60000
|
||||
, 'restart timeout': 'development' == this.env ? 5000 : 60000
|
||||
, 'title': 'cluster'
|
||||
, 'worker title': 'cluster worker'
|
||||
};
|
||||
|
||||
// parent master pid
|
||||
this.ppid = process.env.CLUSTER_PARENT_PID
|
||||
? parseInt(process.env.CLUSTER_PARENT_PID, 10)
|
||||
: null;
|
||||
|
||||
// process is a worker
|
||||
this.isWorker = !! process.env.CLUSTER_MASTER_PID;
|
||||
|
||||
// process is a child (worker or master replacement)
|
||||
this.isChild = this.isWorker || !! process.env.CLUSTER_REPLACEMENT_MASTER;
|
||||
|
||||
// process is master
|
||||
this.isMaster = ! this.isWorker;
|
||||
|
||||
// process id
|
||||
this.pid = process.pid;
|
||||
if (this.isMaster) process.env.CLUSTER_MASTER_PID = this.pid;
|
||||
|
||||
// custom worker fds, defaults to std{out,err}
|
||||
this.customFds = [1, 2];
|
||||
|
||||
// resolve server filename
|
||||
if (this.isWorker && 'string' == typeof this.server) {
|
||||
this.server = require(this.resolve(this.server));
|
||||
}
|
||||
|
||||
// IPC is prepped
|
||||
this.on('start', function(){
|
||||
process.chdir(self.options['working directory']);
|
||||
});
|
||||
|
||||
// spawn our workers
|
||||
this.on('listening', function(){
|
||||
self.spawn(self.options.workers);
|
||||
self.listening = true;
|
||||
});
|
||||
|
||||
// kill children on master exception
|
||||
if (this.isMaster) {
|
||||
process.on('uncaughtException', function(err){
|
||||
self.kill('SIGKILL');
|
||||
console.error(err.stack || String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Interit from `EventEmitter.prototype`.
|
||||
*/
|
||||
|
||||
Master.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/**
|
||||
* Worker is a receiver.
|
||||
*/
|
||||
|
||||
require('./mixins/receiver')(Master.prototype);
|
||||
|
||||
/**
|
||||
* Resolve `path` relative to the server file being executed.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.resolve = function(path){
|
||||
return '/' == path[0]
|
||||
? path
|
||||
: this.dir + '/' + path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` when the environment set by `Master#in()`
|
||||
* matches __NODE_ENV__.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.__defineGetter__('environmentMatches', function(){
|
||||
if (this._env)
|
||||
return this.env == this._env || 'all' == this._env;
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Invoke masters's `method` with worker `id`. (called from Worker)
|
||||
*
|
||||
* @param {Number} id
|
||||
* @param {String} method
|
||||
* @param {...} args
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.call = function(id, method){
|
||||
this.sock = this.sock || dgram.createSocket('udp4');
|
||||
|
||||
var msg = new Buffer(utils.frame({
|
||||
args: utils.toArray(arguments, 2)
|
||||
, method: method
|
||||
, id: id
|
||||
}));
|
||||
|
||||
this.sock.send(
|
||||
msg
|
||||
, 0
|
||||
, msg.length
|
||||
, this.options['socket port']
|
||||
, this.options['socket addr']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform setup tasks then invoke `fn()` when present.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Master} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.start = function(fn){
|
||||
var self = this;
|
||||
|
||||
// deferred title
|
||||
process.title = this.options.title;
|
||||
|
||||
// prevent listen
|
||||
if (this.preventDefault) return this;
|
||||
|
||||
// env match
|
||||
if (this.environmentMatches) {
|
||||
// worker process
|
||||
if (this.isWorker) {
|
||||
this.worker = new Worker(this);
|
||||
this.worker.start();
|
||||
// master process
|
||||
} else if (fn) {
|
||||
fn();
|
||||
// standalone
|
||||
} else {
|
||||
this.on('start', function(){ self.emit('listening'); });
|
||||
if (this.isChild) this.acceptFd();
|
||||
this.setupIPC();
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defer `http.Server#listen()` call.
|
||||
*
|
||||
* @param {Number|String} port or unix domain socket path
|
||||
* @param {String|Function} host or callback
|
||||
* @param {Function} callback
|
||||
* @return {Master} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.listen = function(port, host, callback){
|
||||
var self = this;
|
||||
if (!this.environmentMatches) return this;
|
||||
if ('function' == typeof host) callback = host, host = null;
|
||||
this.port = port;
|
||||
this.host = host;
|
||||
this.callback = callback;
|
||||
return this.start(function(){
|
||||
self.on('start', function(){
|
||||
self.startListening(!self.isChild);
|
||||
});
|
||||
|
||||
if (self.isChild) {
|
||||
self.acceptFd();
|
||||
} else {
|
||||
self.createSocket(function(err, fd){
|
||||
if (err) throw err;
|
||||
self.fd = fd;
|
||||
self.setupIPC();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create / return IPC socket.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.IPCSocket = function(){
|
||||
var self = this;
|
||||
if (this._sock) return this._sock;
|
||||
this._sock = dgram.createSocket('udp4');
|
||||
this._sock.on('message', function(msg, info){
|
||||
try {
|
||||
msg = JSON.parse(msg.toString('ascii'));
|
||||
self.invoke(msg.method, msg.args, self.children[msg.id]);
|
||||
} catch (err) {
|
||||
console.error(err.stack || String(err));
|
||||
}
|
||||
});
|
||||
return this._sock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup IPC.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.setupIPC = function(){
|
||||
var self = this;
|
||||
|
||||
// signal handlers
|
||||
this.registerSignalHandlers();
|
||||
|
||||
// Default worker to the # of cpus
|
||||
this.defaultWorkers();
|
||||
|
||||
// udp server for IPC
|
||||
this.IPCSocket().on('listening', function(){
|
||||
process.nextTick(function(){
|
||||
self.emit('start');
|
||||
});
|
||||
});
|
||||
|
||||
// bind
|
||||
this.IPCSocket().bind(
|
||||
this.options['socket port']
|
||||
, this.options['socket addr']);
|
||||
};
|
||||
|
||||
/**
|
||||
* Conditionally perform the following action, if
|
||||
* __NODE_ENV__ matches `env`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cluster(server)
|
||||
* .in('development').use(cluster.debug())
|
||||
* .in('development').listen(3000)
|
||||
* .in('production').listen(80);
|
||||
*
|
||||
* @param {String} env
|
||||
* @return {Master} self or stubs
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.in = function(env){
|
||||
this._env = env;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set option `key` to `val`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {Mixed} val
|
||||
* @return {Master} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.set = function(key, val){
|
||||
if (this.environmentMatches) this.options[key] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke `fn(master)`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.do = function(fn){
|
||||
if (this.environmentMatches) fn.call(this, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `option` has been set.
|
||||
*
|
||||
* @param {String} option
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.has = function(option){
|
||||
return !! this.options[option];
|
||||
};
|
||||
|
||||
/**
|
||||
* Use the given `plugin`.
|
||||
*
|
||||
* @param {Function} plugin
|
||||
* @return {Master} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.use = function(plugin){
|
||||
if (this.environmentMatches) {
|
||||
this.plugins.push(plugin);
|
||||
if (this.isWorker) {
|
||||
plugin.enableInWorker && plugin(this);
|
||||
} else {
|
||||
plugin(this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create listening socket and callback `fn(err, fd)`.
|
||||
*
|
||||
* @return {Function} fn
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.createSocket = function(fn){
|
||||
var self = this
|
||||
, ipv;
|
||||
|
||||
// explicit host
|
||||
if (this.host) {
|
||||
// ip
|
||||
if (ipv = net.isIP(this.host)) {
|
||||
fn(null, socket('tcp' + ipv));
|
||||
// lookup
|
||||
} else {
|
||||
require('dns').lookup(this.host, function(err, ip, ipv){
|
||||
if (err) return fn(err);
|
||||
self.host = ip;
|
||||
fn(null, socket('tcp' + ipv));
|
||||
});
|
||||
}
|
||||
// local socket
|
||||
} else if ('string' == typeof this.port) {
|
||||
fn(null, socket('unix'));
|
||||
// only port
|
||||
} else if ('number' == typeof this.port) {
|
||||
fn(null, socket('tcp4'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register signal handlers.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.registerSignalHandlers = function(){
|
||||
var self = this;
|
||||
process.on('SIGINT', this.destroy.bind(this));
|
||||
process.on('SIGTERM', this.destroy.bind(this));
|
||||
process.on('SIGQUIT', this.close.bind(this));
|
||||
process.on('SIGUSR2', this.attemptRestart.bind(this));
|
||||
process.on('SIGCHLD', this.maintainWorkerCount.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Default workers to the number of cpus available.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.defaultWorkers = function(){
|
||||
if (!this.has('workers')) {
|
||||
this.set('workers', os
|
||||
? os.cpus().length
|
||||
: 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Restart workers only, sending `signal` defaulting
|
||||
* to __SIGQUIT__.
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.restartWorkers = function(signal){
|
||||
this.kill(signal || 'SIGQUIT');
|
||||
};
|
||||
|
||||
/**
|
||||
* Maintain worker count, re-spawning if necessary.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.maintainWorkerCount = function(){
|
||||
this.children.forEach(function(worker){
|
||||
var pid = worker.proc.pid;
|
||||
if (!pid) this.workerKilled(worker);
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove `n` workers with `signal`
|
||||
* defaulting to __SIGQUIT__.
|
||||
*
|
||||
* @param {Number} n
|
||||
* @param {String} signal
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.remove = function(n, signal){
|
||||
if (!arguments.length) n = 1;
|
||||
var len = this.children.length
|
||||
, worker;
|
||||
|
||||
// cap at worker len
|
||||
if (n > len) n = len;
|
||||
|
||||
// remove the workers
|
||||
while (n--) {
|
||||
worker = this.children.pop();
|
||||
worker.proc.kill(signal || 'SIGQUIT');
|
||||
this.emit('worker removed', worker);
|
||||
this.removeWorker(worker.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove worker `id`.
|
||||
*
|
||||
* @param {Number} id
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.removeWorker = function(id){
|
||||
var worker = this.children[id];
|
||||
if (!worker) return;
|
||||
if (worker.fds) {
|
||||
close(worker.fds[0]);
|
||||
close(worker.fds[1]);
|
||||
}
|
||||
delete this.children[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn `n` workers.
|
||||
*
|
||||
* @param {Number} n
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.spawn = function(n){
|
||||
if (!arguments.length) n = 1;
|
||||
while (n--) this.spawnWorker();
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn a worker with optional `id`.
|
||||
*
|
||||
* @param {Number} id
|
||||
* @return {Worker}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.spawnWorker = function(id){
|
||||
var worker;
|
||||
|
||||
// id given
|
||||
if ('number' == typeof id) {
|
||||
worker = new Worker(this).spawn(id)
|
||||
this.children[id] = worker;
|
||||
worker.id = id;
|
||||
// generate an id
|
||||
} else {
|
||||
worker = new Worker(this).spawn(this.children.length);
|
||||
this.children.push(worker);
|
||||
}
|
||||
|
||||
var obj = {
|
||||
method: 'connect'
|
||||
, args: [worker.id, this.options]
|
||||
};
|
||||
|
||||
worker.sock.write(utils.frame(obj), 'ascii', this.fd);
|
||||
|
||||
// emit
|
||||
this.emit('worker', worker);
|
||||
|
||||
return worker;
|
||||
};
|
||||
|
||||
/**
|
||||
* Graceful shutdown, wait for all workers
|
||||
* to reply before exiting.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.close = function(){
|
||||
this.state = 'graceful shutdown';
|
||||
this.emit('closing');
|
||||
this.kill('SIGQUIT');
|
||||
this.pendingDeaths = this.children.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hard shutdwn, immediately kill all workers.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.destroy = function(){
|
||||
this.state = 'hard shutdown';
|
||||
this.emit('closing');
|
||||
this.kill('SIGKILL');
|
||||
this._destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt restart, while respecting the `restart threshold`
|
||||
* setting, to help prevent recursive restarts.
|
||||
*
|
||||
* @param {String} sig
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.attemptRestart = function(sig){
|
||||
var uptime = new Date - this.startup
|
||||
, threshold = this.options['restart threshold']
|
||||
, timeout = this.options['restart timeout'];
|
||||
|
||||
if (this.__restarting) return;
|
||||
|
||||
if (uptime < threshold) {
|
||||
this.__restarting = true;
|
||||
this.emit('cyclic restart');
|
||||
setTimeout(function(self){
|
||||
self.restart(sig);
|
||||
}, timeout, this);
|
||||
} else {
|
||||
this.restart(sig);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Restart all workers, by sending __SIGQUIT__
|
||||
* or `sig` to them, enabling master to re-spawn.
|
||||
*
|
||||
* @param {String} sig
|
||||
* @return {ChildProcess} replacement master process
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.restart = function(sig){
|
||||
var data = {}
|
||||
, proc = this.spawnMaster();
|
||||
|
||||
// pass object to plugins, allowing them
|
||||
// to patch it, and utilize the data in
|
||||
// the new Master
|
||||
this.emit('restarting', data);
|
||||
proc.sock.write(utils.frame({
|
||||
method: 'connectMaster'
|
||||
, args: [sig || 'SIGQUIT']
|
||||
}), 'ascii', this.fd);
|
||||
|
||||
this.on('close', function(){
|
||||
proc.sock.write(utils.frame({
|
||||
method: 'masterKilled'
|
||||
, args: [data]
|
||||
}), 'ascii');
|
||||
});
|
||||
|
||||
return proc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn a new master process.
|
||||
*
|
||||
* @return {ChildProcess}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.spawnMaster = function(){
|
||||
var fds = socketpair()
|
||||
, customFds = [fds[0], 1, 2]
|
||||
, env = {};
|
||||
|
||||
// merge current env
|
||||
for (var key in process.env) {
|
||||
env[key] = process.env[key];
|
||||
}
|
||||
|
||||
delete env.CLUSTER_MASTER_PID;
|
||||
env.CLUSTER_REPLACEMENT_MASTER = 1;
|
||||
env.CLUSTER_PARENT_PID = this.pid;
|
||||
|
||||
// spawn new master process
|
||||
var proc = spawn(node, this.cmd, {
|
||||
customFds: customFds
|
||||
, env: env
|
||||
});
|
||||
|
||||
// unix domain socket for ICP + fd passing
|
||||
proc.sock = new net.Socket(fds[1], 'unix');
|
||||
return proc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Master replacement connected.
|
||||
*
|
||||
* @param {String} sig
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.connectMaster = function(sig){
|
||||
var self = this;
|
||||
|
||||
function kill(){
|
||||
process.kill(self.ppid, sig);
|
||||
}
|
||||
|
||||
if (this.listening) return kill();
|
||||
this.on('listening', kill);
|
||||
};
|
||||
|
||||
/**
|
||||
* Original master has died aka 'retired',
|
||||
* we now fire the 'restart' event.
|
||||
*
|
||||
* @param {Object} data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.masterKilled = function(data){
|
||||
this.emit('restart', data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Accept fd from parent master, then `setupIPC()`.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.acceptFd = function(){
|
||||
var self = this
|
||||
, stdin = new net.Socket(0, 'unix');
|
||||
|
||||
// set fd and start master
|
||||
stdin.setEncoding('ascii');
|
||||
stdin.on('fd', function(fd){
|
||||
self.fd = fd;
|
||||
self.setupIPC();
|
||||
});
|
||||
|
||||
// frame commands from the parent master
|
||||
stdin.on('data', this.frame.bind(this));
|
||||
stdin.resume();
|
||||
};
|
||||
|
||||
/**
|
||||
* Close servers and emit 'close' before exiting.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype._destroy = function(){
|
||||
this.IPCSocket().close();
|
||||
if (this.fd) close(this.fd);
|
||||
this.emit('close');
|
||||
process.nextTick(process.exit.bind(process));
|
||||
};
|
||||
|
||||
/**
|
||||
* Worker is connected.
|
||||
*
|
||||
* @param {Worker} worker
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.connect = function(worker){
|
||||
this.emit('worker connected', worker);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start listening, when `shouldBind` is `true` the socket
|
||||
* will be bound, and will start listening for connections.
|
||||
*
|
||||
* @param {Boolean} shouldBind
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.startListening = function(shouldBind){
|
||||
var self = this;
|
||||
|
||||
// remove unix domain socket
|
||||
if ('string' == typeof this.port && shouldBind) {
|
||||
fs.unlink(this.port, function(err){
|
||||
if (err && 'ENOENT' != err.code) throw err;
|
||||
startListening();
|
||||
});
|
||||
} else {
|
||||
startListening();
|
||||
}
|
||||
|
||||
// bind / listen
|
||||
function startListening() {
|
||||
if (shouldBind) {
|
||||
try {
|
||||
bind(self.fd, self.port, self.host);
|
||||
listen(self.fd, self.options.backlog);
|
||||
} catch(e) {
|
||||
self.kill('SIGKILL');
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
self.callback && self.callback();
|
||||
self.emit('listening');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The given `worker` has been killed.
|
||||
* Emit the "worker killed" event, remove
|
||||
* the worker, and re-spawn depending on
|
||||
* the master state.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.workerKilled = function(worker){
|
||||
// if we have many failing workers at boot
|
||||
// then we likely have a serious issue.
|
||||
if (new Date - this.startup < 20000) {
|
||||
if (++this._killed == 20) {
|
||||
console.error('');
|
||||
console.error('Cluster detected over 20 worker deaths in the first');
|
||||
console.error('20 seconds of life, there is most likely');
|
||||
console.error('a serious issue with your server.');
|
||||
console.error('');
|
||||
console.error('aborting.');
|
||||
console.error('');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// emit event
|
||||
this.emit('worker killed', worker);
|
||||
|
||||
// always remove worker
|
||||
this.removeWorker(worker.id);
|
||||
|
||||
// state specifics
|
||||
switch (this.state) {
|
||||
case 'hard shutdown':
|
||||
break;
|
||||
case 'graceful shutdown':
|
||||
--this.pendingDeaths || this._destroy();
|
||||
break;
|
||||
default:
|
||||
this.spawnWorker(worker.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* `worker` received exception `err`.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.workerException = function(worker, err){
|
||||
this.emit('worker exception', worker, err);
|
||||
};
|
||||
|
||||
/**
|
||||
* Received worker timeout.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.workerTimeout = function(worker, timeout){
|
||||
this.emit('worker timeout', worker, timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* Worker waiting on `connections` to close.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Master.prototype.workerWaiting = function(worker, connections){
|
||||
this.emit('worker waiting', worker, connections);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send `sig` to all worker processes, defaults to __SIGTERM__.
|
||||
*
|
||||
* @param {String} sig
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Master.prototype.kill = function(sig){
|
||||
var self = this;
|
||||
this.emit('kill', sig);
|
||||
this.children.forEach(function(worker){
|
||||
worker.proc.kill(sig);
|
||||
});
|
||||
};
|
||||
55
node_modules/cluster/lib/mixins/receiver.js
generated
vendored
Normal file
55
node_modules/cluster/lib/mixins/receiver.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
|
||||
/*!
|
||||
* Cluster - receiver mixin
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
module.exports = function(obj){
|
||||
|
||||
/**
|
||||
* Initialize buffer.
|
||||
*/
|
||||
|
||||
obj._buf = '';
|
||||
|
||||
/**
|
||||
* Frame incoming command, buffering the given `chunk`
|
||||
* until a frame is complete. Frames are delimited by a
|
||||
* line feed.
|
||||
*
|
||||
* @param {String} chunk
|
||||
* @api private
|
||||
*/
|
||||
|
||||
obj.frame = function(chunk){
|
||||
for (var i = 0, len = chunk.length; i < len; ++i) {
|
||||
if ('\n' == chunk[i]) {
|
||||
var worker
|
||||
, obj = JSON.parse(this._buf);
|
||||
this._buf = '';
|
||||
if ('number' == typeof obj.id) worker = this.children[obj.id];
|
||||
this.invoke(obj.method, obj.args, worker);
|
||||
} else {
|
||||
this._buf += chunk[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke `method` with the given `args`.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {Mixed} args
|
||||
* @param {Worker} worker
|
||||
* @api private
|
||||
*/
|
||||
|
||||
obj.invoke = function(method, args, worker){
|
||||
if (!method) return;
|
||||
if (!Array.isArray(args)) args = [args];
|
||||
if (worker) args.unshift(worker);
|
||||
if (!this[method]) throw new Error('method ' + method + '() does not exist');
|
||||
this[method].apply(this, args);
|
||||
};
|
||||
};
|
||||
230
node_modules/cluster/lib/plugins/cli.js
generated
vendored
Normal file
230
node_modules/cluster/lib/plugins/cli.js
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
|
||||
/*!
|
||||
* Cluster - cli
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, Log = require('log');
|
||||
|
||||
/**
|
||||
* Commands.
|
||||
*/
|
||||
|
||||
var commands = [];
|
||||
|
||||
/**
|
||||
* Adds a command-line interface to your cluster.
|
||||
*
|
||||
* This plugin requires that you use `pidfiles()`
|
||||
* above `cli()`, so that the pidfile directory
|
||||
* is exposed.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.pidfiles())
|
||||
* .use(cluster.cli())
|
||||
* .listen(3000);
|
||||
*
|
||||
* Once set up our server script serves as both
|
||||
* the master, and the CLI. For example we may
|
||||
* still launch the server(s) as shown below.
|
||||
*
|
||||
* $ nohup node server.js &
|
||||
*
|
||||
* However now we may also utilize commands
|
||||
* provided by this plugin.
|
||||
*
|
||||
* $ node server.js status
|
||||
*
|
||||
* master 3281 dead
|
||||
* worker 0 3282 dead
|
||||
*
|
||||
* For more command information use `--help`.
|
||||
*
|
||||
* $ node server.js --help
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function(){
|
||||
return function(master){
|
||||
requirePIDs(master);
|
||||
|
||||
// augment master
|
||||
master.killall = function(sig){
|
||||
var pid = master.pidof('master');
|
||||
try {
|
||||
// signal master
|
||||
process.kill(pid, sig);
|
||||
} catch (err) {
|
||||
if ('ESRCH' != err.code) throw err;
|
||||
// signal children individually
|
||||
master.workerpids().forEach(function(pid){
|
||||
try {
|
||||
process.kill(pid, sig);
|
||||
} catch (err) {
|
||||
if ('ESRCH' != err.code) throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var args = process.argv.slice(2)
|
||||
, len = commands.length
|
||||
, command
|
||||
, arg;
|
||||
|
||||
// parse arguments
|
||||
while (args.length) {
|
||||
arg = args.shift();
|
||||
for (var i = 0; i < len; ++i) {
|
||||
command = commands[i];
|
||||
if (~command.flags.indexOf(arg)) {
|
||||
command.callback(master);
|
||||
master.preventDefault = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define command `name` with the given callback `fn(master)`
|
||||
* and a short `desc`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @param {String} desc
|
||||
* @return {Object} exports for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var define = exports.define = function(name, fn, desc){
|
||||
commands.push({
|
||||
flags: name.split(/ *, */)
|
||||
, desc: desc
|
||||
, callback: fn
|
||||
});
|
||||
return exports;
|
||||
};
|
||||
|
||||
/**
|
||||
* Report master / worker status based on
|
||||
* the PID files saved by the pidfiles()
|
||||
* plugin.
|
||||
*/
|
||||
|
||||
define('-s, --status, status', function(master){
|
||||
var dir = master.pidfiles
|
||||
, files = fs.readdirSync(dir);
|
||||
|
||||
// null signal failed previous
|
||||
// to this release
|
||||
if (process.version < 'v0.4.1') {
|
||||
console.log('status will not work with node < 0.4.1');
|
||||
console.log('due to SIGTERM globbering the null signal');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log();
|
||||
|
||||
// only pids
|
||||
files.filter(function(file){
|
||||
return file.match(/\.pid$/);
|
||||
// check status
|
||||
}).forEach(function(file){
|
||||
var name = file.replace('.pid', '')
|
||||
, pid = master.pidof(name)
|
||||
, name = name.replace('.', ' ')
|
||||
, color
|
||||
, status;
|
||||
|
||||
try {
|
||||
process.kill(pid, 0);
|
||||
status = 'alive';
|
||||
color = '36';
|
||||
} catch (err) {
|
||||
if ('ESRCH' == err.code) {
|
||||
color = '31';
|
||||
status = 'dead';
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' %s\033[90m %d\033[0m \033[' + color + 'm%s\033[0m'
|
||||
, name
|
||||
, pid
|
||||
, status);
|
||||
});
|
||||
|
||||
console.log();
|
||||
}, 'Output cluster status');
|
||||
|
||||
/**
|
||||
* Restart workers.
|
||||
*/
|
||||
|
||||
define('-r, --restart, restart', function(master){
|
||||
master.killall('SIGUSR2');
|
||||
}, 'Restart master by sending the SIGUSR2 signal');
|
||||
|
||||
/**
|
||||
* Graceful shutdown.
|
||||
*/
|
||||
|
||||
define('-g, --shutdown, shutdown', function(master){
|
||||
master.killall('SIGQUIT');
|
||||
}, 'Graceful shutdown by sending the SIGQUIT signal');
|
||||
|
||||
/**
|
||||
* Hard shutdown.
|
||||
*/
|
||||
|
||||
define('-S, --stop, stop', function(master){
|
||||
master.killall('SIGTERM');
|
||||
}, 'Hard shutdown by sending the SIGTERM signal');
|
||||
|
||||
/**
|
||||
* Display help information.
|
||||
*/
|
||||
|
||||
define('-h, --help, help', function(master){
|
||||
console.log('\n Usage: node <file> <command>\n');
|
||||
commands.forEach(function(command){
|
||||
console.log(' '
|
||||
+ command.flags.join(', ')
|
||||
+ '\n '
|
||||
+ '\033[90m' + command.desc + '\033[0m'
|
||||
+ '\n');
|
||||
});
|
||||
console.log();
|
||||
}, 'Show help information');
|
||||
|
||||
/**
|
||||
* Output cluster version.
|
||||
*/
|
||||
|
||||
define('-v, --version', function(master){
|
||||
console.log(require('../cluster').version);
|
||||
}, 'Output cluster version');
|
||||
|
||||
/**
|
||||
* Require `pidfiles()` plugin.
|
||||
*
|
||||
* @param {Master} master
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function requirePIDs(master) {
|
||||
if (master.pidfiles) return;
|
||||
throw new Error('cli() plugin requires pidfiles(), please add pidfiles() before cli()');
|
||||
}
|
||||
125
node_modules/cluster/lib/plugins/debug.js
generated
vendored
Normal file
125
node_modules/cluster/lib/plugins/debug.js
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
/*!
|
||||
* Cluster - debug
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enable verbose debugging output.
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function(options){
|
||||
options = options || {};
|
||||
|
||||
// strip colors
|
||||
|
||||
function color(text) {
|
||||
if (options.colors === false) return text.replace(/\033\[\d+m/g, '');
|
||||
return text
|
||||
}
|
||||
|
||||
// logger
|
||||
|
||||
var log = {
|
||||
debug: function(str){
|
||||
console.error(color(' \033[90mdebug - %s\033[0m'), str);
|
||||
},
|
||||
info: function(str){
|
||||
console.error(color(' info \033[90m- %s\033[0m'), str);
|
||||
},
|
||||
warning: function(str){
|
||||
console.error(color(' \033[33mwarning\033[0m \033[90m- %s\033[0m'), str);
|
||||
},
|
||||
error: function(str){
|
||||
console.error(color(' \033[31merror\033[0m \033[90m- %s\033[0m'), str);
|
||||
}
|
||||
};
|
||||
|
||||
return function(master){
|
||||
|
||||
// start
|
||||
master.on('start', function(){
|
||||
log.info('master started');
|
||||
});
|
||||
|
||||
// closing
|
||||
master.on('closing', function(){
|
||||
log.info('shutting down');
|
||||
});
|
||||
|
||||
// close
|
||||
master.on('close', function(){
|
||||
log.info('shutdown complete');
|
||||
});
|
||||
|
||||
// killing workers
|
||||
master.on('kill', function(sig){
|
||||
log.warning('kill(' + (sig || 'SIGTERM') + ')');
|
||||
});
|
||||
|
||||
// worker died
|
||||
master.on('worker killed', function(worker){
|
||||
if ('restarting' == master.state) return;
|
||||
log.warning('worker ' + worker.id + ' died');
|
||||
});
|
||||
|
||||
// worker exception
|
||||
master.on('worker exception', function(worker, err){
|
||||
log.error('worker ' + worker.id + ' uncaught exception ' + err.message);
|
||||
});
|
||||
|
||||
// worker is waiting on connections to be closed
|
||||
master.on('worker waiting', function(worker, connections){
|
||||
log.warning('worker ' + worker.id + ' waiting on ' + connections + ' connections');
|
||||
});
|
||||
|
||||
// worker has timed out
|
||||
master.on('worker timeout', function(worker, timeout){
|
||||
log.warning('worker ' + worker.id + ' timed out after ' + timeout + 'ms');
|
||||
});
|
||||
|
||||
// connection
|
||||
master.on('worker connected', function(worker){
|
||||
log.info('worker ' + worker.id + ' connected');
|
||||
});
|
||||
|
||||
// removed
|
||||
master.on('worker removed', function(worker){
|
||||
log.info('worker ' + worker.id + ' removed');
|
||||
});
|
||||
|
||||
// worker
|
||||
master.on('worker', function(worker){
|
||||
log.info('worker ' + worker.id + ' spawned');
|
||||
});
|
||||
|
||||
// listening
|
||||
master.on('listening', function(){
|
||||
log.info('listening for connections');
|
||||
});
|
||||
|
||||
// cyclic or immediate restart
|
||||
master.on('cyclic restart', function(){
|
||||
log.warning('cyclic restart detected, restarting in ' + master.options['restart timeout'] + 'ms');
|
||||
});
|
||||
|
||||
// restart requested
|
||||
master.on('restarting', function(){
|
||||
log.info('restart requested');
|
||||
});
|
||||
|
||||
// restart complete
|
||||
master.on('restart', function(){
|
||||
log.info('restart complete');
|
||||
});
|
||||
|
||||
// exit
|
||||
process.on('exit', function(){
|
||||
log.debug('exit');
|
||||
});
|
||||
}
|
||||
};
|
||||
150
node_modules/cluster/lib/plugins/logger.js
generated
vendored
Normal file
150
node_modules/cluster/lib/plugins/logger.js
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
|
||||
/*!
|
||||
* Cluster - logger
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, Log = require('log')
|
||||
, mkdir = require('mkdirp').mkdirp;
|
||||
|
||||
/**
|
||||
* Enable stdout / stderr logs for both the master
|
||||
* process, as well as workers.
|
||||
*
|
||||
* These output to the given `dir`, or `./logs`
|
||||
* relative to the server's file.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // log to ./logs
|
||||
* engine(server)
|
||||
* .use(engine.logger())
|
||||
* .listen(3000);
|
||||
*
|
||||
* // log to ./app/logs
|
||||
* engine(server)
|
||||
* .use(engine.logger('./app/logs'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* // log to /var/log/node
|
||||
* engine(server)
|
||||
* .use(engine.logger('/var/log/node'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* @param {String} dir
|
||||
* @param {Number} level
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function(dir, level){
|
||||
return function(master){
|
||||
dir = master.resolve(dir || 'logs');
|
||||
|
||||
mkdir(dir, 0755, function(err){
|
||||
if (err) throw err;
|
||||
// master log
|
||||
var stream = fs.createWriteStream(dir + '/master.log', { flags: 'a' });
|
||||
var log = master.log = new Log(level || Log.INFO, stream);
|
||||
|
||||
// master events
|
||||
master.on('start', function(){
|
||||
log.info('master started');
|
||||
});
|
||||
|
||||
// master is shutting down
|
||||
master.on('closing', function(){
|
||||
log.warning('shutting down master');
|
||||
});
|
||||
|
||||
// master has closed and performed cleanup
|
||||
master.on('close', function(){
|
||||
log.info('shutdown complete');
|
||||
});
|
||||
|
||||
// sending signal to all workers
|
||||
master.on('kill', function(sig){
|
||||
log.warning('sent kill(%s) to all workers', sig);
|
||||
});
|
||||
|
||||
// worker was killed
|
||||
master.on('worker killed', function(worker){
|
||||
if ('restarting' == master.state) return;
|
||||
log.error('worker %s died', worker.id);
|
||||
});
|
||||
|
||||
// worker exception
|
||||
master.on('worker exception', function(worker, err){
|
||||
log.error('worker %s uncaught exception %s', worker.id, err.message);
|
||||
});
|
||||
|
||||
// worker is waiting on connections to be closed
|
||||
master.on('worker waiting', function(worker, connections){
|
||||
log.info('worker %s waiting on %s connections', worker.id, connections);
|
||||
});
|
||||
|
||||
// worker has timed out
|
||||
master.on('worker timeout', function(worker, timeout){
|
||||
log.warning('worker %s timed out after %sms', worker.id, timeout);
|
||||
});
|
||||
|
||||
// worker connected to master
|
||||
master.on('worker connected', function(worker){
|
||||
log.debug('worker %s connected', worker.id);
|
||||
});
|
||||
|
||||
// cyclic or immediate restart
|
||||
master.on('cyclic restart', function(){
|
||||
log.warning('cyclic restart detected, restarting in %sms'
|
||||
, master.options['restart timeout']);
|
||||
});
|
||||
|
||||
// restart requested
|
||||
master.on('restarting', function(){
|
||||
log.info('restart requested');
|
||||
});
|
||||
|
||||
// restart complete
|
||||
master.on('restart', function(){
|
||||
log.info('restart complete');
|
||||
});
|
||||
|
||||
// repl socket connection established
|
||||
master.on('repl socket', function(sock){
|
||||
var from = sock.remoteAddress
|
||||
? 'from ' + sock.remoteAddress
|
||||
: '';
|
||||
sock.on('connect', function(){
|
||||
log.info('repl connection %s', from);
|
||||
});
|
||||
sock.on('close', function(){
|
||||
log.info('repl disconnect %s', from);
|
||||
});
|
||||
});
|
||||
|
||||
// override fds
|
||||
master.customFds = [-1, -1];
|
||||
|
||||
// children
|
||||
master.on('worker', function(worker){
|
||||
var proc = worker.proc;
|
||||
|
||||
log.info('spawned worker ' + worker.id);
|
||||
|
||||
// worker log streams
|
||||
var access = fs.createWriteStream(dir + '/workers.access.log', { flags: 'a' })
|
||||
, error = fs.createWriteStream(dir + '/workers.error.log', { flags: 'a' });
|
||||
|
||||
// redirect stdout / stderr
|
||||
proc.stdout.pipe(access);
|
||||
proc.stderr.pipe(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
83
node_modules/cluster/lib/plugins/pidfiles.js
generated
vendored
Normal file
83
node_modules/cluster/lib/plugins/pidfiles.js
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
/*!
|
||||
* Cluster - pidfiles
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, mkdir = require('mkdirp').mkdirp;
|
||||
|
||||
/**
|
||||
* Save pidfiles to the given `dir` or `./pids`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // save to ./pids
|
||||
* cluster(server)
|
||||
* .use(cluster.pidfiles())
|
||||
* .listen(3000);
|
||||
*
|
||||
* // save to /tmp
|
||||
* cluster(server)
|
||||
* .use(cluster.pidfiles('/tmp'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* // save to /var/run/node
|
||||
* cluster(server)
|
||||
* .use(cluster.logger('/var/run/node'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* @param {String} dir
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function(dir){
|
||||
return function(master){
|
||||
dir = master.pidfiles = master.resolve(dir || 'pids');
|
||||
|
||||
// augment master
|
||||
master.pidof = function(name){
|
||||
var dir = master.pidfiles
|
||||
, path = dir + '/' + name + '.pid'
|
||||
, pid = fs.readFileSync(path, 'ascii');
|
||||
|
||||
return parseInt(pid, 10);
|
||||
};
|
||||
|
||||
master.workerpids = function(){
|
||||
var dir = master.pidfiles;
|
||||
return fs.readdirSync(dir).filter(function(file){
|
||||
return file.match(/^worker\./);
|
||||
}).map(function(file){
|
||||
return parseInt(fs.readFileSync(dir + '/' + file, 'ascii'), 10);
|
||||
});
|
||||
};
|
||||
|
||||
mkdir(dir, 0755, function(err){
|
||||
if (err) throw err;
|
||||
|
||||
// save worker pids
|
||||
master.on('worker', function(worker){
|
||||
var path = dir + '/worker.' + worker.id + '.pid';
|
||||
fs.writeFile(path, worker.proc.pid.toString(), 'ascii', function(err){
|
||||
if (err) throw err;
|
||||
master.emit('worker pidfile');
|
||||
});
|
||||
});
|
||||
|
||||
master.on('listening', function(){
|
||||
// save master pid
|
||||
fs.writeFile(dir + '/master.pid', process.pid.toString(), 'ascii', function(err){
|
||||
if (err) throw err;
|
||||
master.emit('pidfile');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
118
node_modules/cluster/lib/plugins/reload.js
generated
vendored
Normal file
118
node_modules/cluster/lib/plugins/reload.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/*!
|
||||
* Cluster - reload
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, basename = require('path').basename
|
||||
, extname = require('path').extname;
|
||||
|
||||
/**
|
||||
* Restart the server the given js `files` have changed.
|
||||
* `files` may be several directories, filenames, etc,
|
||||
* defaulting to the server's root directory.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `signal` Signal defaulting to __SIGTERM__
|
||||
* - `interval` Watcher interval, defaulting to `100`
|
||||
* - `extensions` File extensions to watch, defaults to ['.js']
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.reload())
|
||||
* .listen(3000);
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.reload('lib'))
|
||||
* .listen(3000);
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.reload(['lib', 'tests', 'index.js']))
|
||||
* .listen(3000);
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.reload('lib', { interval: 60000 }))
|
||||
* .listen(3000);
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.reload('lib', { extensions: ['.js', '.coffee'] }))
|
||||
* .listen(3000);
|
||||
*
|
||||
* Ignore Directories:
|
||||
*
|
||||
* By default `reload()` will ignore the following directories:
|
||||
*
|
||||
* - node_modules
|
||||
* - support
|
||||
* - examples
|
||||
* - test
|
||||
* - bin
|
||||
*
|
||||
* Alter with `reload.ignoreDirectories`
|
||||
*
|
||||
* cluster.reload.ignoreDirectories.push('src');
|
||||
*
|
||||
* @param {String|Array} files
|
||||
* @param {Options} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function(files, options){
|
||||
options = options || {};
|
||||
|
||||
// defaults
|
||||
var interval = options.interval || 100
|
||||
, extensions = options.extensions || ['.js']
|
||||
, signal = options.signal || 'SIGTERM';
|
||||
|
||||
return function(master){
|
||||
if (!files) files = master.dir;
|
||||
if (!Array.isArray(files)) files = [files];
|
||||
files.forEach(traverse);
|
||||
|
||||
// traverse file if it is a directory
|
||||
// otherwise setup the watcher
|
||||
function traverse(file) {
|
||||
file = master.resolve(file);
|
||||
fs.stat(file, function(err, stat){
|
||||
if (!err) {
|
||||
if (stat.isDirectory()) {
|
||||
if (~exports.ignoreDirectories.indexOf(basename(file))) return;
|
||||
fs.readdir(file, function(err, files){
|
||||
files.map(function(f){
|
||||
return file + '/' + f;
|
||||
}).forEach(traverse);
|
||||
});
|
||||
} else {
|
||||
watch(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// watch file for changes
|
||||
function watch(file) {
|
||||
if (!~extensions.indexOf(extname(file))) return;
|
||||
fs.watchFile(file, { interval: interval }, function(curr, prev){
|
||||
if (curr.mtime > prev.mtime) {
|
||||
console.log(' \033[36mchanged\033[0m \033[90m- %s\033[0m', file);
|
||||
master.restartWorkers(signal);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Directories to ignore.
|
||||
*/
|
||||
|
||||
exports.ignoreDirectories = ['node_modules', 'support', 'test', 'bin'];
|
||||
196
node_modules/cluster/lib/plugins/repl.js
generated
vendored
Normal file
196
node_modules/cluster/lib/plugins/repl.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
|
||||
/*!
|
||||
* Cluster - repl
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var net = require('net')
|
||||
, repl = require('repl');
|
||||
|
||||
/**
|
||||
* Enable REPL with all arguments passed to `net.Server#listen()`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cluster(server)
|
||||
* .use(cluster.stats())
|
||||
* .use(cluster.repl('/var/run/cluster'))
|
||||
* .listen();
|
||||
*
|
||||
* In the terminal:
|
||||
*
|
||||
* $ sudo telnet /var/run/cluster
|
||||
*
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports = module.exports = function(){
|
||||
var args = arguments;
|
||||
if (!args.length) throw new Error('repl() plugin requires port/host or path');
|
||||
return function(master){
|
||||
var server
|
||||
, sockets = [];
|
||||
|
||||
|
||||
// start repl
|
||||
function start(){
|
||||
// TCP or unix-domain socket repl
|
||||
server = net.createServer(function(sock){
|
||||
sockets.push(sock);
|
||||
var ctx = repl.start('cluster> ', sock).context;
|
||||
master.emit('repl socket', sock);
|
||||
|
||||
// augment socket to provide some formatting methods
|
||||
sock.title = function(str){ this.write('\n \033[36m' + str + '\033[0m\n'); }
|
||||
sock.row = function(key, val){ this.write(' \033[90m' + key + ':\033[0m ' + val + '\n'); }
|
||||
|
||||
// merge commands into context
|
||||
// executing in context of master
|
||||
Object.keys(exports).forEach(function(cmd){
|
||||
ctx[cmd] = function(){
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(master, sock);
|
||||
return exports[cmd].apply(master, args);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// Apply all arguments given
|
||||
server.listen.apply(server, args);
|
||||
}
|
||||
|
||||
// initial master starts immediately
|
||||
// replacements starts when the previous
|
||||
// has closed
|
||||
master.on(master.isChild
|
||||
? 'restart'
|
||||
: 'start', start);
|
||||
|
||||
// restart notification
|
||||
master.on('restarting', function(){
|
||||
sockets.forEach(function(sock){
|
||||
if (sock.fd) {
|
||||
sock.write('\n\033[33mrestarting\033[0m - closing connection soon\n');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// close
|
||||
master.on('close', function(){
|
||||
sockets.forEach(function(sock){
|
||||
sock.fd && sock.end();
|
||||
});
|
||||
if (server && server.fd) server.close();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Define function `name`, with the given callback
|
||||
* `fn(master, sock, ...)` and `description`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @param {String} desc
|
||||
* @return {Object} exports for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
var define = exports.define = function(name, fn, desc){
|
||||
(exports[name] = fn).description = desc;
|
||||
return exports;
|
||||
};
|
||||
|
||||
/**
|
||||
* Display commmand help.
|
||||
*/
|
||||
|
||||
define('help', function(master, sock){
|
||||
sock.title('Commands');
|
||||
Object.keys(exports).forEach(function(cmd){
|
||||
if ('define' == cmd) return;
|
||||
|
||||
var fn = exports[cmd]
|
||||
, params = fn.toString().match(/^function +\((.*?)\)/)[1]
|
||||
, params = params.split(/ *, */).slice(2);
|
||||
|
||||
sock.row(
|
||||
cmd + '(' + params.join(', ') + ')'
|
||||
, fn.description);
|
||||
});
|
||||
sock.write('\n');
|
||||
}, 'Display help information');
|
||||
|
||||
/**
|
||||
* Spawn `n` additional workers with the given `signal`.
|
||||
*/
|
||||
|
||||
define('spawn', function(master, sock, n, signal){
|
||||
n = n || 1;
|
||||
if (n < 0) {
|
||||
n = Math.abs(n);
|
||||
sock.write('removing ' + n + ' worker' + (n > 1 ? 's' : '')
|
||||
+ ' with ' + (signal || 'SIGQUIT') + '\n');
|
||||
master.remove(n, signal);
|
||||
} else {
|
||||
sock.write('spawning ' + n + ' worker' + (n > 1 ? 's' : '') + '\n');
|
||||
master.spawn(n);
|
||||
}
|
||||
}, 'Spawn one or more additional workers, or remove one or more');
|
||||
|
||||
/**
|
||||
* Output process ids.
|
||||
*/
|
||||
|
||||
define('pids', function(master, sock){
|
||||
sock.title('pids');
|
||||
sock.row('master', process.pid);
|
||||
master.children.forEach(function(worker){
|
||||
sock.row('worker #' + worker.id, worker.proc.pid);
|
||||
});
|
||||
sock.write('\n');
|
||||
}, 'Output process ids');
|
||||
|
||||
/**
|
||||
* Kill the given worker by `id` and `signal`.
|
||||
*/
|
||||
|
||||
define('kill', function(master, sock, id, signal){
|
||||
var worker = master.children[id];
|
||||
if (worker) {
|
||||
worker.proc.kill(signal);
|
||||
sock.write('sent \033[36m' + (signal || 'SIGTERM') + '\033[0m to worker #' + id + '\n');
|
||||
} else {
|
||||
sock.write('invalid worker id\n');
|
||||
}
|
||||
}, 'Send signal or SIGTERM to the given worker');
|
||||
|
||||
/**
|
||||
* Gracefully shutdown.
|
||||
*/
|
||||
|
||||
define('shutdown', function(master, sock){
|
||||
master.close();
|
||||
}, 'Gracefully shutdown server');
|
||||
|
||||
/**
|
||||
* Hard shutdown.
|
||||
*/
|
||||
|
||||
define('stop', function(master, sock){
|
||||
master.destroy();
|
||||
}, 'Hard shutdown');
|
||||
|
||||
/**
|
||||
* Gracefully restart all workers.
|
||||
*/
|
||||
|
||||
define('restart', function(master, sock){
|
||||
master.restart();
|
||||
}, 'Gracefully restart all workers');
|
||||
250
node_modules/cluster/lib/plugins/stats.js
generated
vendored
Normal file
250
node_modules/cluster/lib/plugins/stats.js
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
|
||||
/*!
|
||||
* Cluster - stats
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, Log = require('log')
|
||||
, repl = require('./repl')
|
||||
, utils = require('../utils')
|
||||
, os = require('os');
|
||||
|
||||
/**
|
||||
* Enable stat tracking with the given `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `connections` enable connection statistics
|
||||
* - `requests` enable request statistics
|
||||
* - `lightRequests` enable light-weight request statistics
|
||||
*
|
||||
* Real-time applications should utilize `lightRequests` for reporting
|
||||
* when possible, although less data is available.
|
||||
*
|
||||
* TODO: UDP
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function(options){
|
||||
options = options || {};
|
||||
stats.enableInWorker = options.connections || options.requests;
|
||||
|
||||
function stats(master){
|
||||
var server = master.server;
|
||||
master.connectionStats = options.connections;
|
||||
master.requestStats = options.requests;
|
||||
master.lightRequestStats = options.lightRequests;
|
||||
|
||||
// worker stats
|
||||
if (master.isWorker) {
|
||||
var id = 0;
|
||||
|
||||
// connections
|
||||
if (options.connections) {
|
||||
server.on('connection', function(sock){
|
||||
var data = { remoteAddress: sock.remoteAddress };
|
||||
master.call('reportStats', 'connection', data);
|
||||
sock.on('close', function(){
|
||||
master.call('reportStats', 'disconnection', data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// light-weight requests
|
||||
if (options.lightRequests) {
|
||||
utils.unshiftListener(server, 'request', function(req, res){
|
||||
master.call('reportStats', 'light request', res.id = ++id);
|
||||
var end = res.end;
|
||||
res.end = function(str, encoding){
|
||||
res.end = end;
|
||||
res.end(str, encoding);
|
||||
master.call('reportStats', 'light request complete', res.id);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// requests
|
||||
if (options.requests) {
|
||||
utils.unshiftListener(server, 'request', function(req, res){
|
||||
var data = {
|
||||
remoteAddress: req.socket.remoteAddress
|
||||
, headers: req.headers
|
||||
, httpVersion: req.httpVersion
|
||||
, method: req.method
|
||||
, url: req.url
|
||||
, id: ++id
|
||||
};
|
||||
|
||||
master.call('reportStats', 'request', data);
|
||||
|
||||
var end = res.end;
|
||||
res.end = function(str, encoding){
|
||||
res.end = end;
|
||||
res.end(str, encoding);
|
||||
master.call('reportStats', 'request complete', data);
|
||||
};
|
||||
});
|
||||
}
|
||||
// master stats
|
||||
} else {
|
||||
master.stats = {
|
||||
start: new Date
|
||||
, restarts: 0
|
||||
, workersSpawned: 0
|
||||
, workersKilled: 0
|
||||
};
|
||||
|
||||
// 0.4.x
|
||||
if (os) {
|
||||
master.stats.totalmem = os.totalmem();
|
||||
master.stats.freemem = os.freemem();
|
||||
}
|
||||
|
||||
// worker stats
|
||||
master.reportStats = function(worker, type, data){
|
||||
master.emit('client ' + type, worker, data);
|
||||
switch (type) {
|
||||
case 'connection':
|
||||
worker.stats.connectionsTotal++;
|
||||
worker.stats.connectionsActive++;
|
||||
break;
|
||||
case 'disconnection':
|
||||
worker.stats.connectionsActive--;
|
||||
break;
|
||||
case 'light request':
|
||||
case 'request':
|
||||
worker.stats.requestsTotal++;
|
||||
}
|
||||
};
|
||||
|
||||
// total workers spawned
|
||||
master.on('worker', function(worker){
|
||||
++master.stats.workersSpawned;
|
||||
worker.stats = {
|
||||
start: new Date
|
||||
, connectionsTotal: 0
|
||||
, connectionsActive: 0
|
||||
, requestsTotal: 0
|
||||
};
|
||||
});
|
||||
|
||||
// total worker deaths
|
||||
master.on('worker killed', function(worker){
|
||||
++master.stats.workersKilled;
|
||||
});
|
||||
|
||||
// restarting
|
||||
master.on('restarting', function(data){
|
||||
++master.stats.restarts;
|
||||
data.stats = master.stats;
|
||||
});
|
||||
|
||||
// restart
|
||||
master.on('restart', function(data){
|
||||
master.stats = data.stats;
|
||||
master.stats.start = new Date(master.stats.start);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* REPL statistics command.
|
||||
*/
|
||||
|
||||
repl.define('stats', function(master, sock){
|
||||
var active = master.children.length
|
||||
, total = master.stats.workersSpawned
|
||||
, deaths = master.stats.workersKilled
|
||||
, restarts = master.stats.restarts;
|
||||
|
||||
// master stats
|
||||
sock.title('Master');
|
||||
if (os) sock.row('os', os.type() + ' ' + os.release());
|
||||
sock.row('state', master.state);
|
||||
sock.row('started', master.stats.start.toUTCString());
|
||||
sock.row('uptime', utils.formatDateRange(new Date, master.stats.start));
|
||||
sock.row('restarts', restarts);
|
||||
sock.row('workers', active);
|
||||
sock.row('deaths', deaths);
|
||||
|
||||
// resources
|
||||
if (os) {
|
||||
sock.title('Resources');
|
||||
sock.row('load average', os.loadavg().map(function(n){ return n.toFixed(2); }).join(' '));
|
||||
sock.row('cores utilized', active + ' / ' + os.cpus().length);
|
||||
var free = utils.formatBytes(master.stats.freemem);
|
||||
var total = utils.formatBytes(master.stats.totalmem);
|
||||
sock.row('memory at boot (free / total)', free + ' / ' + total);
|
||||
var free = utils.formatBytes(os.freemem());
|
||||
var total = utils.formatBytes(os.totalmem());
|
||||
sock.row('memory now (free / total)', free + ' / ' + total);
|
||||
}
|
||||
|
||||
// worker stats
|
||||
sock.title('Workers');
|
||||
|
||||
// connections
|
||||
if (master.connectionStats) {
|
||||
sock.row('connections total', sum(master.children, 'connectionsTotal'));
|
||||
sock.row('connections active', sum(master.children, 'connectionsActive'));
|
||||
}
|
||||
|
||||
// requests
|
||||
if (master.requestStats) {
|
||||
sock.row('requests total', sum(master.children, 'requestsTotal'));
|
||||
}
|
||||
|
||||
master.children.forEach(function(worker){
|
||||
var stats = ''
|
||||
, piped = [];
|
||||
|
||||
// uptime
|
||||
stats += utils.formatDateRange(new Date, worker.stats.start);
|
||||
|
||||
// connections
|
||||
if (master.connectionStats) {
|
||||
piped.push(worker.stats.connectionsActive);
|
||||
piped.push(worker.stats.connectionsTotal);
|
||||
}
|
||||
|
||||
// requests
|
||||
if (master.requestStats) {
|
||||
piped.push(worker.stats.requestsTotal);
|
||||
}
|
||||
|
||||
if (piped.length) {
|
||||
stats += ' ' + piped.join('\033[90m|\033[0m');
|
||||
}
|
||||
|
||||
sock.row(worker.id, stats);
|
||||
});
|
||||
sock.write('\n');
|
||||
}, 'Display server statistics');
|
||||
|
||||
|
||||
/**
|
||||
* Return sum of each `prop` in `arr`.
|
||||
*
|
||||
* @param {Array} arr
|
||||
* @param {String} prop
|
||||
* @return {Number}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function sum(arr, prop){
|
||||
return arr.reduce(function(sum, obj){
|
||||
return sum + obj.stats[prop];
|
||||
}, 0);
|
||||
};
|
||||
98
node_modules/cluster/lib/utils.js
generated
vendored
Normal file
98
node_modules/cluster/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
|
||||
/*!
|
||||
* Cluster - utils
|
||||
* Copyright (c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Frame the given `obj`.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.frame = function(obj){
|
||||
return JSON.stringify(obj) + '\n';
|
||||
};
|
||||
|
||||
/**
|
||||
* Fast alternative to `Array.prototype.slice.call()`.
|
||||
*
|
||||
* @param {Arguments} args
|
||||
* @param {Number} index
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.toArray = function(args, index){
|
||||
var arr = []
|
||||
, len = args.length;
|
||||
for (var i = (index || 0); i < len; ++i) {
|
||||
arr.push(args[i]);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format byte-size.
|
||||
*
|
||||
* @param {Number} bytes
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.formatBytes = function(bytes) {
|
||||
var kb = 1024
|
||||
, mb = 1024 * kb
|
||||
, gb = 1024 * mb;
|
||||
if (bytes < kb) return bytes + 'b';
|
||||
if (bytes < mb) return (bytes / kb).toFixed(2) + 'kb';
|
||||
if (bytes < gb) return (bytes / mb).toFixed(2) + 'mb';
|
||||
return (bytes / gb).toFixed(2) + 'gb';
|
||||
};
|
||||
|
||||
/**
|
||||
* Format date difference between `a` and `b`.
|
||||
*
|
||||
* @param {Date} a
|
||||
* @param {Date} b
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.formatDateRange = function(a, b) {
|
||||
var diff = a > b ? a - b : b - a
|
||||
, second = 1000
|
||||
, minute = second * 60
|
||||
, hour = minute * 60
|
||||
, day = hour * 24;
|
||||
|
||||
function unit(name, n) {
|
||||
return n + ' ' + name + (1 == n ? '' : 's');
|
||||
}
|
||||
|
||||
if (diff < second) return unit('millisecond', diff);
|
||||
if (diff < minute) return unit('second', (diff / second).toFixed(0));
|
||||
if (diff < hour) return unit('minute', (diff / minute).toFixed(0));
|
||||
if (diff < day) return unit('hour', (diff / hour).toFixed(0));
|
||||
return unit('day', (diff / day).toFixed(1));
|
||||
};
|
||||
|
||||
/**
|
||||
* Unshift a callback.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} event
|
||||
* @param {String} fn
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.unshiftListener = function(obj, event, fn){
|
||||
if (Array.isArray(obj._events[event])) {
|
||||
obj._events[event].unshift(fn);
|
||||
} else {
|
||||
obj._events[event] = [fn, obj._events[event]];
|
||||
}
|
||||
};
|
||||
232
node_modules/cluster/lib/worker.js
generated
vendored
Normal file
232
node_modules/cluster/lib/worker.js
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
|
||||
/*!
|
||||
* Cluster - Worker
|
||||
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
, spawn = require('child_process').spawn
|
||||
, binding = process.binding('net')
|
||||
, utils = require('./utils')
|
||||
, net = require('net');
|
||||
|
||||
/**
|
||||
* Node binary.
|
||||
*/
|
||||
|
||||
var node = process.execPath;
|
||||
|
||||
/**
|
||||
* Initialize a new `Worker` with the given `master`.
|
||||
*
|
||||
* Signals:
|
||||
*
|
||||
* - `SIGINT` immediately exit
|
||||
* - `SIGTERM` immediately exit
|
||||
* - `SIGQUIT` graceful exit
|
||||
*
|
||||
* @param {Master} master
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Worker = module.exports = function Worker(master) {
|
||||
this.master = master;
|
||||
this.server = master.server;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `EventEmitter.prototype`.
|
||||
*/
|
||||
|
||||
Worker.prototype.__proto__ = EventEmitter.prototype;
|
||||
|
||||
/**
|
||||
* Worker is a receiver.
|
||||
*/
|
||||
|
||||
require('./mixins/receiver')(Worker.prototype);
|
||||
|
||||
/**
|
||||
* Start worker.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.start = function(){
|
||||
var self = this
|
||||
, call = this.master.call;
|
||||
|
||||
// proxy to provide worker id
|
||||
this.master.call = function(){
|
||||
var args = utils.toArray(arguments);
|
||||
// Allow calling master methods that
|
||||
// don't take worker as first argument
|
||||
if (false !== args[0]) args.unshift(self.id);
|
||||
return call.apply(this, args);
|
||||
};
|
||||
|
||||
// stdin
|
||||
this.stdin = new net.Socket(0, 'unix');
|
||||
this.stdin.setEncoding('ascii');
|
||||
this.stdin.on('data', this.frame.bind(this));
|
||||
this.stdin.resume();
|
||||
|
||||
// demote usr/group
|
||||
if (this.server && this.server.listenFD) {
|
||||
this.server.on('listening', function(){
|
||||
var group = self.options.group;
|
||||
if (group) process.setgid(group);
|
||||
var user = self.options.user;
|
||||
if (user) process.setuid(user);
|
||||
});
|
||||
|
||||
// stdin
|
||||
this.stdin.on('fd', this.server.listenFD.bind(this.server));
|
||||
}
|
||||
|
||||
// signal handlers
|
||||
process.on('SIGINT', this.destroy.bind(this));
|
||||
process.on('SIGTERM', this.destroy.bind(this));
|
||||
process.on('SIGQUIT', this.close.bind(this));
|
||||
|
||||
// conditionally handle uncaughtException
|
||||
process.nextTick(function(){
|
||||
if (!process.listeners('uncaughtException').length) {
|
||||
process.on('uncaughtException', function(err){
|
||||
// stderr for logs
|
||||
console.error(err.stack || err.message);
|
||||
|
||||
// report exception
|
||||
self.master.call('workerException', err);
|
||||
|
||||
// exit
|
||||
process.nextTick(function(){
|
||||
self.destroy();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Received connect event, set the worker `id`
|
||||
* and `options`.
|
||||
*
|
||||
* @param {String} id
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.connect = function(id, options){
|
||||
this.options = options;
|
||||
|
||||
// worker id
|
||||
this.id = id;
|
||||
|
||||
// timeout
|
||||
this.timeout = options.timeout;
|
||||
|
||||
// title
|
||||
process.title = options['worker title'].replace('{n}', id);
|
||||
|
||||
// notify master of connection
|
||||
this.master.call('connect');
|
||||
};
|
||||
|
||||
/**
|
||||
* Immediate shutdown.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.destroy = function(){
|
||||
this.emit('close');
|
||||
process.nextTick(process.exit);
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform graceful shutdown.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.close = function(){
|
||||
var self = this
|
||||
, server = this.server;
|
||||
|
||||
if (server && server.connections) {
|
||||
// stop accepting
|
||||
server.watcher.stop();
|
||||
|
||||
// check pending connections
|
||||
setInterval(function(){
|
||||
self.master.call('workerWaiting', server.connections);
|
||||
server.connections || self.destroy();
|
||||
}, 2000);
|
||||
|
||||
// timeout
|
||||
if (this.timeout) {
|
||||
setTimeout(function(){
|
||||
self.master.call('workerTimeout', self.timeout);
|
||||
self.destroy();
|
||||
}, this.timeout);
|
||||
}
|
||||
} else {
|
||||
this.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn the worker with the given `id`.
|
||||
*
|
||||
* @param {Number} id
|
||||
* @return {Worker} for chaining
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.spawn = function(id){
|
||||
var fds = binding.socketpair()
|
||||
, customFds = [fds[0]].concat(this.master.customFds)
|
||||
, env = {};
|
||||
|
||||
// merge env
|
||||
for (var key in process.env) {
|
||||
env[key] = process.env[key];
|
||||
}
|
||||
|
||||
this.id = env.CLUSTER_WORKER = id;
|
||||
|
||||
// spawn worker process
|
||||
this.proc = spawn(
|
||||
node
|
||||
, this.master.cmd
|
||||
, { customFds: customFds, env: env });
|
||||
|
||||
// unix domain socket for ICP + fd passing
|
||||
this.sock = new net.Socket(fds[1], 'unix');
|
||||
|
||||
// saving file descriptors for later use
|
||||
this.fds = fds;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoke worker's `method` (called from Master).
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {...} args
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Worker.prototype.call = function(method){
|
||||
this.sock.write(utils.frame({
|
||||
args: utils.toArray(arguments, 1)
|
||||
, method: method
|
||||
}), 'ascii');
|
||||
};
|
||||
Reference in New Issue
Block a user