mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 03:26:04 +01:00
zero downtime and reload support with cluster module
This commit is contained in:
7
app.js
7
app.js
@@ -157,6 +157,13 @@ function start() {
|
|||||||
process.on('SIGTERM', shutdown);
|
process.on('SIGTERM', shutdown);
|
||||||
process.on('SIGINT', shutdown);
|
process.on('SIGINT', shutdown);
|
||||||
process.on('SIGHUP', restart);
|
process.on('SIGHUP', restart);
|
||||||
|
process.on('message', function(message) {
|
||||||
|
switch(message) {
|
||||||
|
case 'reload':
|
||||||
|
meta.reload();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
process.on('uncaughtException', function(err) {
|
process.on('uncaughtException', function(err) {
|
||||||
winston.error(err.message);
|
winston.error(err.message);
|
||||||
console.log(err.stack);
|
console.log(err.stack);
|
||||||
|
|||||||
45
loader.js
45
loader.js
@@ -95,7 +95,8 @@ var nconf = require('nconf'),
|
|||||||
// nbb, nbbOld;
|
// nbb, nbbOld;
|
||||||
|
|
||||||
var Loader = {
|
var Loader = {
|
||||||
timesStarted: 0
|
timesStarted: 0,
|
||||||
|
shutdown_queue: []
|
||||||
};
|
};
|
||||||
|
|
||||||
Loader.init = function() {
|
Loader.init = function() {
|
||||||
@@ -106,11 +107,6 @@ Loader.init = function() {
|
|||||||
silent: process.env.NODE_ENV !== 'development' ? true : false
|
silent: process.env.NODE_ENV !== 'development' ? true : false
|
||||||
});
|
});
|
||||||
|
|
||||||
for(var x=0;x<numCPUs;x++) {
|
|
||||||
// Only the first worker sets up templates/sounds/jobs/etc
|
|
||||||
cluster.fork({ cluster_setup: x === 0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
cluster.on('fork', function(worker) {
|
cluster.on('fork', function(worker) {
|
||||||
worker.on('message', function(message) {
|
worker.on('message', function(message) {
|
||||||
if (message && typeof message === 'object' && message.action) {
|
if (message && typeof message === 'object' && message.action) {
|
||||||
@@ -118,6 +114,12 @@ Loader.init = function() {
|
|||||||
case 'ready':
|
case 'ready':
|
||||||
console.log('[cluster] Child Process (' + worker.process.pid + ') listening for connections.');
|
console.log('[cluster] Child Process (' + worker.process.pid + ') listening for connections.');
|
||||||
worker.send('bind');
|
worker.send('bind');
|
||||||
|
|
||||||
|
// Kill an instance in the shutdown queue
|
||||||
|
var workerToKill = Loader.shutdown_queue.pop();
|
||||||
|
if (workerToKill) {
|
||||||
|
cluster.workers[workerToKill].kill();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'restart':
|
case 'restart':
|
||||||
console.log('[cluster] Restarting...');
|
console.log('[cluster] Restarting...');
|
||||||
@@ -125,6 +127,10 @@ Loader.init = function() {
|
|||||||
console.log('[cluster] Restarting...');
|
console.log('[cluster] Restarting...');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case 'reload':
|
||||||
|
console.log('[cluster] Reloading...');
|
||||||
|
Loader.reload();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -147,19 +153,36 @@ Loader.init = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log('[cluster] Child Process (' + worker.process.pid + ') has exited (code: ' + code + ')');
|
console.log('[cluster] Child Process (' + worker.process.pid + ') has exited (code: ' + code + ')');
|
||||||
|
if (!worker.suicide) {
|
||||||
|
console.log('[cluster] Spinning up another process...')
|
||||||
cluster.fork();
|
cluster.fork();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('SIGHUP', Loader.restart);
|
process.on('SIGHUP', Loader.restart);
|
||||||
|
|
||||||
|
Loader.start();
|
||||||
// fs.writeFile(__dirname + '/pidfile', process.pid);
|
// fs.writeFile(__dirname + '/pidfile', process.pid);
|
||||||
};
|
};
|
||||||
|
|
||||||
Loader.restart = function(callback) {
|
Loader.start = function() {
|
||||||
async.eachSeries(Object.keys(cluster.workers), function(id, next) {
|
for(var x=0;x<numCPUs;x++) {
|
||||||
cluster.workers[id].kill();
|
// Only the first worker sets up templates/sounds/jobs/etc
|
||||||
next();
|
cluster.fork({ cluster_setup: x === 0 });
|
||||||
}, callback);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader.restart = function(callback) {
|
||||||
|
// Slate existing workers for termination -- welcome to death row.
|
||||||
|
Loader.shutdown_queue = Loader.shutdown_queue.concat(Object.keys(cluster.workers));
|
||||||
|
Loader.start();
|
||||||
|
};
|
||||||
|
|
||||||
|
Loader.reload = function() {
|
||||||
|
Object.keys(cluster.workers).forEach(function(worker_id) {
|
||||||
|
cluster.workers[worker_id].send('reload');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Loader.init();
|
Loader.init();
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ var async = require('async'),
|
|||||||
emitter.emit('nodebb:ready');
|
emitter.emit('nodebb:ready');
|
||||||
}
|
}
|
||||||
|
|
||||||
callback.apply(null, arguments);
|
if (callback) callback.apply(null, arguments);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var mkdirp = require('mkdirp'),
|
|||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
|
cluster = require('cluster'),
|
||||||
|
|
||||||
emitter = require('../emitter'),
|
emitter = require('../emitter'),
|
||||||
plugins = require('../plugins'),
|
plugins = require('../plugins'),
|
||||||
@@ -13,6 +14,16 @@ var mkdirp = require('mkdirp'),
|
|||||||
Templates = {};
|
Templates = {};
|
||||||
|
|
||||||
Templates.compile = function(callback) {
|
Templates.compile = function(callback) {
|
||||||
|
if (cluster.isWorker && process.env.cluster_setup !== 'true') {
|
||||||
|
return setTimeout(function() {
|
||||||
|
console.log('FAKING TEMPLATE COMPILE');
|
||||||
|
emitter.emit('templates:compiled');
|
||||||
|
if (callback) callback();
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
console.log('REAL TEMPLATE COMPILE');
|
||||||
|
}
|
||||||
|
|
||||||
var baseTemplatesPath = nconf.get('base_templates_path'),
|
var baseTemplatesPath = nconf.get('base_templates_path'),
|
||||||
viewsPath = nconf.get('views_dir'),
|
viewsPath = nconf.get('views_dir'),
|
||||||
themeTemplatesPath = nconf.get('theme_templates_path');
|
themeTemplatesPath = nconf.get('theme_templates_path');
|
||||||
|
|||||||
@@ -128,14 +128,7 @@ module.exports = function(app, data) {
|
|||||||
|
|
||||||
routeCurrentTheme(app, data.currentThemeId, data.themesData);
|
routeCurrentTheme(app, data.currentThemeId, data.themesData);
|
||||||
routeThemeScreenshots(app, data.themesData);
|
routeThemeScreenshots(app, data.themesData);
|
||||||
|
|
||||||
if (process.env.cluster_setup === 'true') {
|
|
||||||
meta.templates.compile();
|
meta.templates.compile();
|
||||||
} else {
|
|
||||||
setTimeout(function() {
|
|
||||||
emitter.emit('templates:compiled');
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return middleware;
|
return middleware;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ var groups = require('../groups'),
|
|||||||
async = require('async'),
|
async = require('async'),
|
||||||
winston = require('winston'),
|
winston = require('winston'),
|
||||||
index = require('./index'),
|
index = require('./index'),
|
||||||
|
cluster = require('cluster'),
|
||||||
|
|
||||||
SocketAdmin = {
|
SocketAdmin = {
|
||||||
user: require('./admin/user'),
|
user: require('./admin/user'),
|
||||||
@@ -39,7 +40,13 @@ SocketAdmin.before = function(socket, method, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.reload = function(socket, data, callback) {
|
SocketAdmin.reload = function(socket, data, callback) {
|
||||||
|
if (cluster.isWorker) {
|
||||||
|
process.send({
|
||||||
|
action: 'reload'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
meta.reload(callback);
|
meta.reload(callback);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketAdmin.restart = function(socket, data, callback) {
|
SocketAdmin.restart = function(socket, data, callback) {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ if(nconf.get('ssl')) {
|
|||||||
emailer.registerApp(app);
|
emailer.registerApp(app);
|
||||||
notifications.init();
|
notifications.init();
|
||||||
|
|
||||||
if (process.env.cluster_setup === 'true') {
|
if (cluster.isWorker && process.env.cluster_setup === 'true') {
|
||||||
user.startJobs();
|
user.startJobs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ if(nconf.get('ssl')) {
|
|||||||
meta.js.minify(app.enabled('minification'));
|
meta.js.minify(app.enabled('minification'));
|
||||||
meta.css.minify();
|
meta.css.minify();
|
||||||
|
|
||||||
if (process.env.cluster_setup === 'true') {
|
if (cluster.isWorker && process.env.cluster_setup === 'true') {
|
||||||
meta.sounds.init();
|
meta.sounds.init();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user