mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 11:05:54 +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); | ||||||
|   | |||||||
							
								
								
									
										47
									
								
								loader.js
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								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,20 +153,37 @@ 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 + ')'); | ||||||
| 		cluster.fork(); | 		if (!worker.suicide) { | ||||||
|  | 			console.log('[cluster] Spinning up another process...') | ||||||
|  | 			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(); | ||||||
|  |  | ||||||
| // Start the daemon! | // Start the daemon! | ||||||
|   | |||||||
| @@ -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); | ||||||
|  | 	meta.templates.compile(); | ||||||
| 	if (process.env.cluster_setup === 'true') { |  | ||||||
| 		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) { | ||||||
| 	meta.reload(callback); | 	if (cluster.isWorker) { | ||||||
|  | 		process.send({ | ||||||
|  | 			action: 'reload' | ||||||
|  | 		}); | ||||||
|  | 	} else { | ||||||
|  | 		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