Skip to content

Instantly share code, notes, and snippets.

@psulek
Created December 2, 2015 07:19
Show Gist options
  • Save psulek/ef9726c573a9e827faae to your computer and use it in GitHub Desktop.
Save psulek/ef9726c573a9e827faae to your computer and use it in GitHub Desktop.

Revisions

  1. psulek created this gist Dec 2, 2015.
    124 changes: 124 additions & 0 deletions total-cluster-without-downtimes.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,124 @@
    var http = require('http');
    var path = require('path');
    var cluster = require('cluster');
    var os = require('os');
    var fs = require('fs');

    var debug = true;

    if (!cluster.isMaster) {
    var F = require('total.js');

    // Set framework ID
    F.on('message', function(message) {
    if (message.type === 'id')
    framework.id = message.id;

    if (message.type == 'graceful_shutdown')
    F.stop();
    });

    F.http('release');
    return;
    }

    var shutdown_processing = false;
    var pid = '';
    var directory = process.cwd();

    process.on('SIGTERM', end);
    process.on('SIGINT', end);
    process.on('exit', end);

    function end() {
    fs.unlink(pid, noop);
    process.exit(0);
    }

    function noop() {}

    process.on('SIGHUP', function() {
    if (shutdown_processing) {
    console.log('Already reloading...');
    return;
    }

    shutdown_processing = true;

    console.log('SIGHUP received, reloading workers...');

    var index = 0;
    var workers = Object.keys(cluster.workers);

    var workersKiller = function() {
    if (index == workers.length) {
    console.log('SIGHUP complete.');
    shutdown_processing = false;
    return;
    }

    // Process is already dead
    if (cluster.workers[workers[index]] === undefined) {
    index++;
    workersKiller();
    return;
    }

    console.log('killing worker %d', cluster.workers[workers[index]].process.pid);

    var shutdown_timer;

    // Graceful shutdown total.js
    cluster.workers[workers[index]].once('disconnect', function() {
    console.log('Worker %d disconnected', this.process.pid);
    clearTimeout(shutdown_timer);
    });
    cluster.workers[workers[index]].send({ type: 'graceful_shutdown' });
    cluster.workers[workers[index]].disconnect();

    shutdown_timer = setTimeout(function(timeout_worker) {
    console.log('could not close connections in time, forcefully shutting down (master)');
    timeout_worker.kill();
    }, 5000, cluster.workers[workers[index]]);

    // Spawn a new worker
    var fork = cluster.fork();
    fork.on('message', onMessage);
    fork.send({ type: 'id', id: i });
    fork.once('listening', function() {
    console.log('Worker replaced.');
    index++;
    workersKiller();
    });
    };

    workersKiller();
    });

    var numCPUs = os.cpus().length;

    // Spawn total.js based on CPU count
    for (var i = 0; i < numCPUs; i++) {
    var fork = cluster.fork();
    fork.on('message', onMessage);
    fork.send({ type: 'id', id: i });
    }

    function onMessage(message) {
    console.log('Message ->', message);
    }

    if (process.pid > 0) {
    console.log('MASTER PID: ' + process.pid);
    pid = path.join(directory, 'release.pid');
    fs.writeFileSync(pid, process.pid);

    pidInterval = setInterval(function() {
    fs.exists(pid, function(exist) {
    if (exist)
    return;

    process.exit(0);
    });
    }, 2000);
    }