|
|
|
@ -5,7 +5,8 @@ var nconf = require('nconf'),
|
|
|
|
|
fs = require('fs'),
|
|
|
|
|
url = require('url'),
|
|
|
|
|
path = require('path'),
|
|
|
|
|
cluster = require('cluster'),
|
|
|
|
|
fork = require('child_process').fork,
|
|
|
|
|
|
|
|
|
|
async = require('async'),
|
|
|
|
|
logrotate = require('logrotate-stream'),
|
|
|
|
|
|
|
|
|
@ -15,9 +16,8 @@ var nconf = require('nconf'),
|
|
|
|
|
output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true }),
|
|
|
|
|
silent = process.env.NODE_ENV !== 'development',
|
|
|
|
|
numProcs,
|
|
|
|
|
handles = {},
|
|
|
|
|
handleIndex = 0,
|
|
|
|
|
server,
|
|
|
|
|
|
|
|
|
|
workers = [],
|
|
|
|
|
|
|
|
|
|
Loader = {
|
|
|
|
|
timesStarted: 0,
|
|
|
|
@ -32,11 +32,6 @@ var nconf = require('nconf'),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Loader.init = function(callback) {
|
|
|
|
|
cluster.setupMaster({
|
|
|
|
|
exec: "app.js",
|
|
|
|
|
silent: silent
|
|
|
|
|
});
|
|
|
|
|
Loader.primaryWorker = 1;
|
|
|
|
|
|
|
|
|
|
if (silent) {
|
|
|
|
|
console.log = function(value) {
|
|
|
|
@ -60,84 +55,9 @@ Loader.displayStartupMessages = function(callback) {
|
|
|
|
|
callback();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Loader.addClusterEvents = function(callback) {
|
|
|
|
|
cluster.on('fork', function(worker) {
|
|
|
|
|
worker.on('message', function(message) {
|
|
|
|
|
if (message && typeof message === 'object' && message.action) {
|
|
|
|
|
var otherWorkers;
|
|
|
|
|
|
|
|
|
|
switch (message.action) {
|
|
|
|
|
case 'ready':
|
|
|
|
|
if (Loader.js.cache) {
|
|
|
|
|
worker.send({
|
|
|
|
|
action: 'js-propagate',
|
|
|
|
|
cache: Loader.js.cache,
|
|
|
|
|
map: Loader.js.map,
|
|
|
|
|
hash: Loader.js.hash
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Loader.css.cache) {
|
|
|
|
|
worker.send({
|
|
|
|
|
action: 'css-propagate',
|
|
|
|
|
cache: Loader.css.cache,
|
|
|
|
|
acpCache: Loader.css.acpCache,
|
|
|
|
|
hash: Loader.css.hash
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'restart':
|
|
|
|
|
console.log('[cluster] Restarting...');
|
|
|
|
|
Loader.restart(function(err) {
|
|
|
|
|
console.log('[cluster] Restarting...');
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'reload':
|
|
|
|
|
console.log('[cluster] Reloading...');
|
|
|
|
|
Loader.reload();
|
|
|
|
|
break;
|
|
|
|
|
case 'js-propagate':
|
|
|
|
|
Loader.js.cache = message.cache;
|
|
|
|
|
Loader.js.map = message.map;
|
|
|
|
|
Loader.js.hash = message.hash;
|
|
|
|
|
|
|
|
|
|
Loader.notifyWorkers({
|
|
|
|
|
action: 'js-propagate',
|
|
|
|
|
cache: message.cache,
|
|
|
|
|
map: message.map,
|
|
|
|
|
hash: message.hash
|
|
|
|
|
}, worker.id);
|
|
|
|
|
break;
|
|
|
|
|
case 'css-propagate':
|
|
|
|
|
Loader.css.cache = message.cache;
|
|
|
|
|
Loader.css.acpCache = message.acpCache;
|
|
|
|
|
Loader.css.hash = message.hash;
|
|
|
|
|
|
|
|
|
|
Loader.notifyWorkers({
|
|
|
|
|
action: 'css-propagate',
|
|
|
|
|
cache: message.cache,
|
|
|
|
|
acpCache: message.acpCache,
|
|
|
|
|
hash: message.hash
|
|
|
|
|
}, worker.id);
|
|
|
|
|
break;
|
|
|
|
|
case 'listening':
|
|
|
|
|
if (message.primary) {
|
|
|
|
|
Loader.primaryWorker = parseInt(worker.id, 10);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'config:update':
|
|
|
|
|
Loader.notifyWorkers(message);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
Loader.addWorkerEvents = function(worker) {
|
|
|
|
|
|
|
|
|
|
cluster.on('listening', function(worker) {
|
|
|
|
|
console.log('[cluster] Child Process (' + worker.process.pid + ') listening for connections.');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cluster.on('exit', function(worker, code, signal) {
|
|
|
|
|
worker.on('exit', function(code, signal) {
|
|
|
|
|
if (code !== 0) {
|
|
|
|
|
if (Loader.timesStarted < numProcs*3) {
|
|
|
|
|
Loader.timesStarted++;
|
|
|
|
@ -153,118 +73,134 @@ Loader.addClusterEvents = function(callback) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[cluster] Child Process (' + worker.process.pid + ') has exited (code: ' + code + ', signal: ' + signal +')');
|
|
|
|
|
console.log('[cluster] Child Process (' + worker.pid + ') has exited (code: ' + code + ', signal: ' + signal +')');
|
|
|
|
|
if (!worker.suicide) {
|
|
|
|
|
console.log('[cluster] Spinning up another process...');
|
|
|
|
|
console.log('[cluster] Spinning up another process...');
|
|
|
|
|
|
|
|
|
|
var wasPrimary = parseInt(worker.id, 10) === Loader.primaryWorker;
|
|
|
|
|
forkWorker(wasPrimary);
|
|
|
|
|
forkWorker(worker.index, worker.isPrimary);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cluster.on('disconnect', function(worker) {
|
|
|
|
|
console.log('[cluster] Child Process (' + worker.process.pid + ') has disconnected');
|
|
|
|
|
});
|
|
|
|
|
worker.on('message', function(message) {
|
|
|
|
|
if (message && typeof message === 'object' && message.action) {
|
|
|
|
|
var otherWorkers;
|
|
|
|
|
|
|
|
|
|
callback();
|
|
|
|
|
switch (message.action) {
|
|
|
|
|
case 'ready':
|
|
|
|
|
if (Loader.js.cache) {
|
|
|
|
|
worker.send({
|
|
|
|
|
action: 'js-propagate',
|
|
|
|
|
cache: Loader.js.cache,
|
|
|
|
|
map: Loader.js.map,
|
|
|
|
|
hash: Loader.js.hash
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Loader.css.cache) {
|
|
|
|
|
worker.send({
|
|
|
|
|
action: 'css-propagate',
|
|
|
|
|
cache: Loader.css.cache,
|
|
|
|
|
acpCache: Loader.css.acpCache,
|
|
|
|
|
hash: Loader.css.hash
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'restart':
|
|
|
|
|
console.log('[cluster] Restarting...');
|
|
|
|
|
Loader.restart(function(err) {
|
|
|
|
|
console.log('[cluster] Restarting...');
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'reload':
|
|
|
|
|
console.log('[cluster] Reloading...');
|
|
|
|
|
Loader.reload();
|
|
|
|
|
break;
|
|
|
|
|
case 'js-propagate':
|
|
|
|
|
Loader.js.cache = message.cache;
|
|
|
|
|
Loader.js.map = message.map;
|
|
|
|
|
Loader.js.hash = message.hash;
|
|
|
|
|
|
|
|
|
|
Loader.notifyWorkers({
|
|
|
|
|
action: 'js-propagate',
|
|
|
|
|
cache: message.cache,
|
|
|
|
|
map: message.map,
|
|
|
|
|
hash: message.hash
|
|
|
|
|
}, worker.pid);
|
|
|
|
|
break;
|
|
|
|
|
case 'css-propagate':
|
|
|
|
|
Loader.css.cache = message.cache;
|
|
|
|
|
Loader.css.acpCache = message.acpCache;
|
|
|
|
|
Loader.css.hash = message.hash;
|
|
|
|
|
|
|
|
|
|
Loader.notifyWorkers({
|
|
|
|
|
action: 'css-propagate',
|
|
|
|
|
cache: message.cache,
|
|
|
|
|
acpCache: message.acpCache,
|
|
|
|
|
hash: message.hash
|
|
|
|
|
}, worker.pid);
|
|
|
|
|
break;
|
|
|
|
|
case 'listening':
|
|
|
|
|
if (message.primary) {
|
|
|
|
|
Loader.primaryWorker = parseInt(worker.pid, 10);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'config:update':
|
|
|
|
|
Loader.notifyWorkers(message);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Loader.start = function(callback) {
|
|
|
|
|
console.log('Clustering enabled: Spinning up ' + numProcs + ' process(es).\n');
|
|
|
|
|
|
|
|
|
|
for(var x=0; x<numProcs; ++x) {
|
|
|
|
|
forkWorker(x === 0);
|
|
|
|
|
for (var x=0; x<numProcs; ++x) {
|
|
|
|
|
forkWorker(x, x === 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var urlObject = url.parse(nconf.get('url'));
|
|
|
|
|
var port = urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567;
|
|
|
|
|
nconf.set('port', port);
|
|
|
|
|
|
|
|
|
|
server = net.createServer(function(connection) {
|
|
|
|
|
// remove this once node 0.12.x ships, see https://github.com/elad/node-cluster-socket.io/issues/4
|
|
|
|
|
connection._handle.readStop();
|
|
|
|
|
|
|
|
|
|
var workers = clusterWorkers();
|
|
|
|
|
var worker = workers[workerIndex(connection.remoteAddress, numProcs)];
|
|
|
|
|
|
|
|
|
|
if (worker) {
|
|
|
|
|
handles[handleIndex] = connection._handle;
|
|
|
|
|
|
|
|
|
|
worker.send({action: 'sticky-session:connection', handleIndex: handleIndex}, connection);
|
|
|
|
|
handleIndex ++;
|
|
|
|
|
} else {
|
|
|
|
|
console.log('Cant find worker! Worker count : ' + workers.length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}).listen(port);
|
|
|
|
|
|
|
|
|
|
if (callback) {
|
|
|
|
|
callback();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function forkWorker(isPrimary) {
|
|
|
|
|
var worker = cluster.fork({
|
|
|
|
|
cluster_setup: isPrimary,
|
|
|
|
|
handle_jobs: isPrimary
|
|
|
|
|
}),
|
|
|
|
|
output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true });
|
|
|
|
|
|
|
|
|
|
if (silent) {
|
|
|
|
|
worker.process.stdout.pipe(output);
|
|
|
|
|
worker.process.stderr.pipe(output);
|
|
|
|
|
}
|
|
|
|
|
function forkWorker(index, isPrimary) {
|
|
|
|
|
var urlObject = url.parse(nconf.get('url'));
|
|
|
|
|
var port = urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567;
|
|
|
|
|
|
|
|
|
|
worker.on('message', function(message) {
|
|
|
|
|
if (!message || message.action !== 'sticky-session:accept') {
|
|
|
|
|
return;
|
|
|
|
|
var worker = fork('app.js', [], {
|
|
|
|
|
silent: silent,
|
|
|
|
|
env: {
|
|
|
|
|
isPrimary: isPrimary,
|
|
|
|
|
isCluster: true,
|
|
|
|
|
port: parseInt(port, 10) + index
|
|
|
|
|
}
|
|
|
|
|
var _handle = handles[message.handleIndex];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (_handle) {
|
|
|
|
|
_handle.close();
|
|
|
|
|
worker.index = index;
|
|
|
|
|
worker.isPrimary = isPrimary;
|
|
|
|
|
|
|
|
|
|
delete handles[message.handleIndex];
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
workers[index] = worker;
|
|
|
|
|
|
|
|
|
|
function workerIndex(ip, numProcs) {
|
|
|
|
|
var s = '';
|
|
|
|
|
for (var i = 0, _len = ip.length; i < _len; i++) {
|
|
|
|
|
if (parseInt(ip[i], 10)) {
|
|
|
|
|
s += ip[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Number(s) % numProcs || 0;
|
|
|
|
|
}
|
|
|
|
|
Loader.addWorkerEvents(worker);
|
|
|
|
|
|
|
|
|
|
function clusterWorkers() {
|
|
|
|
|
var workers = [];
|
|
|
|
|
var output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true });
|
|
|
|
|
|
|
|
|
|
for(var i in cluster.workers) {
|
|
|
|
|
workers.push(cluster.workers[i]);
|
|
|
|
|
if (silent) {
|
|
|
|
|
worker.stdout.pipe(output);
|
|
|
|
|
worker.stderr.pipe(output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return workers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Loader.restart = function(callback) {
|
|
|
|
|
console.log('[cluster] closing server');
|
|
|
|
|
|
|
|
|
|
killWorkers();
|
|
|
|
|
|
|
|
|
|
closeHandles();
|
|
|
|
|
|
|
|
|
|
server.close(function() {
|
|
|
|
|
console.log('[cluster] server closed');
|
|
|
|
|
Loader.start();
|
|
|
|
|
});
|
|
|
|
|
Loader.start();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Loader.reload = function() {
|
|
|
|
|
Object.keys(cluster.workers).forEach(function(worker_id) {
|
|
|
|
|
cluster.workers[worker_id].send({
|
|
|
|
|
workers.forEach(function(worker) {
|
|
|
|
|
worker.send({
|
|
|
|
|
action: 'reload'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
@ -275,31 +211,20 @@ Loader.stop = function() {
|
|
|
|
|
|
|
|
|
|
// Clean up the pidfile
|
|
|
|
|
fs.unlinkSync(__dirname + '/pidfile');
|
|
|
|
|
|
|
|
|
|
server.close();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function killWorkers() {
|
|
|
|
|
Object.keys(cluster.workers).forEach(function(id) {
|
|
|
|
|
cluster.workers[id].kill();
|
|
|
|
|
workers.forEach(function(worker) {
|
|
|
|
|
worker.suicide = true;
|
|
|
|
|
worker.kill();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function closeHandles() {
|
|
|
|
|
for(var h in handles) {
|
|
|
|
|
var handle = handles[h];
|
|
|
|
|
if (handle) {
|
|
|
|
|
handle.close();
|
|
|
|
|
delete handles[h];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Loader.notifyWorkers = function (msg, worker_id) {
|
|
|
|
|
worker_id = parseInt(worker_id, 10);
|
|
|
|
|
Object.keys(cluster.workers).forEach(function(id) {
|
|
|
|
|
if (parseInt(id, 10) !== worker_id) {
|
|
|
|
|
cluster.workers[id].send(msg);
|
|
|
|
|
Loader.notifyWorkers = function (msg, worker_pid) {
|
|
|
|
|
worker_pid = parseInt(worker_pid, 10);
|
|
|
|
|
workers.forEach(function(worker) {
|
|
|
|
|
if (parseInt(worker.pid, 10) !== worker_pid) {
|
|
|
|
|
worker.send(msg);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
@ -330,7 +255,6 @@ if (nconf.get('daemon') !== false) {
|
|
|
|
|
async.series([
|
|
|
|
|
Loader.init,
|
|
|
|
|
Loader.displayStartupMessages,
|
|
|
|
|
Loader.addClusterEvents,
|
|
|
|
|
Loader.start
|
|
|
|
|
], function(err) {
|
|
|
|
|
if (err) {
|
|
|
|
|