anyone know how to shut down a node cluster?
When I run this:
var http = require('http');
var server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
});
server.listen(1337, "127.0.0.1");
process.once('SIGQUIT', function() {
console.log("Closing "+process.pid);
server.close();
});
console.log("I am "+process.pid);
Everything works as expected. I send a SIGQUIT to the process, it
closes the server, and because there are no more possible events, node
quits (I love this feature).
A sample output is:
#> node test.js
I am 930
Closing 930
#>
However when I add the following code to the end of the above code:
var cluster = require('cluster');
if (cluster.isMaster) {
var worker = cluster.fork();
process.once('SIGQUIT',function() {
console.log("Passing SIGQUIT to "+worker.pid);
worker.kill('SIGQUIT');
});
}
The output is the following:
#> node test.js
I am 941
Worker 942 online
I am 942
Closing 941
Passing SIGQUIT to 942
Closing 942
After which node is still around, basically forever (for a value of
forever >=3 minutes, which is the limit of my patience).
When looking at the processes, via
#> lsof -p 941,942
I noticed, that the IPC pipe between the processes still seems to be
open. So my question is, how do I tell the master/workers that they
are done with their works and should shut down as soon as their main
work is complete.
What I want is that the http-servers in the workers complete request
that may be in process, and only when they are done, exit. Just like
the single process model does it.
Is this possible, should happen automatically anyway? Anyone have any
insight here?
Regards, Phil
When using the Signal-Handler to fs.close(0) in the worker and
worker._channel.close() in the master the process still remains alive.
So that makes me think it may not be the pipe, or at least not just
the pipe.
Hmmmm
I figured out that this works:
var cluster = require('cluster');
if (cluster.isMaster) {
var worker = cluster.fork();
process.once('SIGQUIT',function() {
console.log("Passing SIGQUIT to "+worker.pid);
worker.kill('SIGQUIT');
console.log("Closing Worker-Channel in Master");
worker._channel.close();
worker._channel.unref();
});
} else {
process.once('SIGQUIT',function() {
console.log("Closing Master-Channel in Child");
process._channel.close();
process._channel.unref();
require("fs").close(0);
});
}
However it uses the "private" _channel of the worker (in the master)
and of process (in the worker), which is subject to change.
Is this just a lacking API?
should we have something like:
// cluster.js # from line 70
var shutdown = false;
function startMaster() {
// This can only be called from the master.
assert(cluster.isMaster);
if (masterStarted) return;
masterStarted = true;
workerFilename = process.argv[1];
workerArgs = process.argv.slice(2);
process.on('uncaughtException', function(e) {
// Quickly try to kill all the workers.
// TODO: be session leader - will cause auto SIGHUP to the
children.
eachWorker(function(worker) {
debug("kill worker " + worker.pid);
worker.kill();
})
console.error("Exception in cluster master process: " +
e.message + '\n' + e.stack);
process.exit(1);
});
cluster.shutdown = function() {
if (shutdown) return;
shutdown = true;
eachWorker(function() {
worker._channel.close();
worker._channel.unref();
});
}
}
// cluster.js # from line 177
var fdClose=require('fs').close;
cluster._startWorker = function() {
assert(cluster.isWorker);
workerId = parseInt(process.env.NODE_WORKER_ID);
queryMaster({ cmd: 'online' });
// Make callbacks from queryMaster()
process.on('message', function(msg, handle) {
debug("recv " + JSON.stringify(msg));
if (shutdown) return;
if (msg.cmd == 'shutdown') {
shutdown = true;
process._channel.close();
process._channel.unref();
fdClose(0);
}
if (msg._queryId && msg._queryId in queryCallbacks) {
var cb = queryCallbacks[msg._queryId];
if (typeof cb == 'function') {
cb(msg, handle);
}
delete queryCallbacks[msg._queryId]
}
});
};
function queryMaster(msg, cb) {
assert(cluster.isWorker);
debug('send ' + JSON.stringify(msg));
if (shutdown) return;
// Grab some random queryId
msg._queryId = (++queryIds);
msg._workerId = workerId;
// Store callback for later. Callback called in _startWorker.
if (cb) {
queryCallbacks[msg._queryId] = cb;
}
// Send message to master.
process.send(msg);
}
On second thought I think I'll move this to nodes-dev.
Regards, Phil