UNPKG

3.17 kBJavaScriptView Raw
1
2'use strict';
3
4module.exports = function (config) {
5 var cluster = require('cluster');
6 var fs = require('fs');
7 var path = require('path');
8 var config = require('./config')(process.env.NODE_ENV, config);
9
10 var SHUTDOWN_TIMEOUT = 10000;
11
12 var init = function (count, callback) {
13 cluster.on('exit', function (worker, code) {
14 if (code !== 0) {
15 config.log.error(worker.process.pid + ' died with error code ' + code + ', starting new worker...');
16 cluster.fork();
17 }
18 });
19
20 var ready = 0;
21 var listening = function () {
22 if (++ready === count) {
23 callback();
24 }
25 };
26
27 for (var i = 0; i < count; ++i) {
28 cluster.fork().on('listening', listening);
29 }
30 };
31
32 var restart = function () {
33 var replace = function (workers) {
34 if (workers.length === 0) {
35 config.log.info('All replacement workers now running');
36 return;
37 }
38
39 var parasite = cluster.workers[workers[0]];
40 var worker = cluster.fork();
41 var pid = parasite.process.pid;
42 var timeout;
43
44 parasite.on('disconnect', function () {
45 config.log.info('Shutdown complete for ' + pid);
46 clearTimeout(timeout);
47 });
48 parasite.disconnect();
49 timeout = setTimeout(function () {
50 config.log.info('Timed out waiting for ' + pid + ' to disconnect, killing process');
51 parasite.send('force exit');
52 }, SHUTDOWN_TIMEOUT);
53
54 worker.on('listening', function () {
55 config.log.info('Created process ' + worker.process.pid + ' to replace ' + pid);
56 replace(workers.slice(1));
57 });
58 };
59
60 replace(Object.keys(cluster.workers));
61 };
62
63 var saveProcessId = function () {
64 var pid = process.pid;
65 fs.writeFile(path.join(config.path.root, 'shunter.pid'), pid, function (err) {
66 if (err) {
67 config.log.error('Error saving shunter.pid file for process ' + pid + ' ' + (err.message || err.toString()));
68 } else {
69 config.log.debug('Saved shunter.pid file for process ' + pid);
70 }
71 });
72 };
73
74 var clearProcessId = function () {
75 config.log.debug('Deleting old shunter.pid file');
76 fs.unlinkSync(path.join(config.path.root, 'shunter.pid'));
77 };
78
79 var saveTimestamp = function () {
80 fs.writeFileSync(path.join(config.path.shunterRoot, 'timestamp.json'), '{"value":' + Date.now() + '}');
81 };
82
83 return {
84 use: function () {
85 config.middleware.push(Array.prototype.slice.call(arguments));
86 },
87 start: function () {
88 if (cluster.isMaster) {
89 var childProcesses = Math.min(
90 require('os').cpus().length,
91 config.argv['max-child-processes']
92 );
93 saveTimestamp();
94 saveProcessId();
95
96 init(childProcesses, function () {
97 config.log.info('Shunter started with ' + childProcesses + ' child processes listening');
98 });
99
100 process.on('SIGUSR2', function () {
101 config.log.debug('SIGUSR2 received, reloading all workers');
102 saveTimestamp();
103 restart();
104 });
105 process.on('SIGINT', function () {
106 config.log.debug('SIGINT received, exiting...');
107 process.exit(0);
108 });
109 process.on('exit', function () {
110 clearProcessId();
111 config.log.info('Goodbye!');
112 });
113 } else {
114 require('./worker')(config);
115 }
116
117 return this;
118 },
119 getConfig: function () {
120 return config;
121 }
122 };
123};