22

Currently, my prod environment for a side project is a git repo, where I pull in some code, manually kill the server with Ctrl-C, and restart it manually.

I realize there are a lot of things wrong with this. For instance, what if a user is still in the middle of doing something important and the process is crunching sensitive data, and I just killed it?!

When I used node v0.4.x there was a nice Cluster module that could restart the server gracefully, when the application is in a quiet state. In v0.6.x the Cluster module is built into node, but it's really, really bare, and doesn't have the graceful restart ability.

Anyone know how I can gracefully restart a nodejs server in v0.6.x?

rgettman
  • 176,041
  • 30
  • 275
  • 357
user1159470
  • 289
  • 1
  • 2
  • 5

3 Answers3

16

You can handle POSIX signals in node code.

See in the example code, that will handle SIGINT (Ctrl-C for instance) as a STOP signal for all cluster workers, and SIGUSR2 will just restart all workers

So, issuing kill -SIGUSR2 PID, where PID is node master PID will restart all cluster

module.exports = function(app) {
    var cluster = require('cluster');
    var numCPUs = require('os').cpus().length;
    var workerList = new Array();
    var sigkill = false;

    if (cluster.isMaster) {
        for (var i = 0; i < numCPUs; i++) {
            var env = process.env;
            var worker = cluster.fork(env);
            workerList.push(worker);
        }

        process.on('SIGUSR2',function(){
            console.log("Received SIGUSR2 from system");
            console.log("There are " + workerList.length + " workers running");
            workerList.forEach(function(worker){
                console.log("Sending STOP message to worker PID=" + worker.pid);
                worker.send({cmd: "stop"});
            });
        });

        process.on('SIGINT',function(){
            sigkill = true;
            process.exit();
        });

        cluster.on('death', function(worker) {
            if (sigkill) {
                logger.warn("SIGKINT received - not respawning workers");
                return;
            }
            var newWorker = cluster.fork();
            console.log('Worker ' + worker.pid + ' died and it will be re-spawned');

            removeWorkerFromListByPID(worker.pid);
            workerList.push(newWorker);
        });
    } else {
        process.on('message', function(msg) {
            if (msg.cmd && msg.cmd == 'stop') {
                console.log("Received STOP signal from master");
                app.close();
                process.exit();
            }
        });
        app.listen(3000);
    }

    function removeWorkerFromListByPID(pid) {
        var counter = -1;
        workerList.forEach(function(worker){
            ++counter;
            if (worker.pid === pid) {
                workerList.splice(counter, 1);
            }
        });
    }
}
DUzun
  • 1,693
  • 17
  • 15
Andriy Samilyak
  • 413
  • 3
  • 7
2

There's a module named Forever.

That can gracefully restart the process. I suppose then you can somehow run several instances with cluster (one on each core) and use Forever to monitor / restart them.

This is just an option I found; I'm open to suggestions!

Michael Dillon
  • 1,037
  • 6
  • 16
user1159470
  • 289
  • 1
  • 2
  • 5
1

There's also a module named PM2. It has the ability to stop all processes in a cluster.

Catfish
  • 18,876
  • 54
  • 209
  • 353
  • -1; I tried out the "graceful" reload capability of PM2 and as far as I can tell it [just plain doesn't work](https://github.com/Unitech/pm2/issues/3078) right now. I can't promise that any of the other answers here work better, but I *am* sure that this one is a frustrating and time-wasting dead end. – Mark Amery Sep 03 '17 at 20:31