Sanka Darshana
Sanka Darshana

Reputation: 1421

Upgrade Node.JS web app without restarting

Is there a way to make changes to the code of a Node.JS application without restarting it.

For an example, if a file is being downloaded from the server at that time, is there a way to upgrade the server without the file transfer being disrupted?

Upvotes: 4

Views: 605

Answers (1)

Laurent Perrin
Laurent Perrin

Reputation: 14881

Yes, it's possible with the cluster module.

Basically, you will start several slave instances of your app sharing the same port through a master process. The master will distribute incoming connections to the slaves.

When you want to restart, the master will, in sequence:

  • stop passing connections to a slave
  • tell it gently to restart
  • wait for it to respawn

Here's some code to get you started (you'll need to npm install async underscore).

Note: This is just to get you started. You should be prepared to handle unexpected slave failures and timeouts in a real setup.

var cluster = require('cluster');
var http = require('http');
var _ = require('underscore');
var async = require('async');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log('starting master with PID', process.pid);
  // Fork workers.
  var slaves = {};

  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('fork', function (worker) {
    slaves[worker.id] = worker;
  });

  cluster.on('exit', function (worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
    delete slaves[worker.id];

    // restart the worker
    cluster.fork();
  });

  process.on('SIGHUP', function restartApp() {
    console.log('restarting all slaves');
    // important: force node.js to reload all js sources
    delete require.cache;

    var toRestart = _(slaves).values();

    async.eachSeries(toRestart, function (slave, done) {
      slave.kill('SIGTERM');

      // when the new worker starts, proceed to the next instance
      cluster.once('listening', function () {
        done();
      });
    });
  });
} else {
  console.log('- starting slave with PID', process.pid);
  // Workers can share any TCP connection
  // In this case its a HTTP server
  var server = http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  });

  process.on('SIGTERM', function () {
    // finish all current connections, then stop
    server.close(function () {
      process.exit(0);
    });
  });

  server.listen(8000);
}

When you call `kill -HUP , you'll see that it restart all slaves in sequence:

worker 5156 died
- starting slave with PID 5164
worker 5157 died
- starting slave with PID 5165
worker 5158 died
- starting slave with PID 5166
worker 5159 died
- starting slave with PID 5167

While restarting, there are always 3 running slaves to handle incoming connections.

One last thing: in case you try, console.log doesn't do anything in the slave SIGTERM handler because it has already been disconnected from the master.

Upvotes: 9

Related Questions