Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js: Worker with differents code in cluster?

I have a application in node.js.

This application is divided in 3 parts:

launcher.js, which start the two other part, and restart them on crash/update after handling cleaning.

app.js, which work on the computer himself.

server.js which is used to access log and different command.

The simplified code for launcher is:

var cluster = require('cluster'),
    exec = require('child_process').exec,
    server;

if (cluster.isMaster) {
    cluster.fork();
    server = exec('server.js');

    cluster.on('exit', function(worker, code, signal) {
        //Clean corrupted data, log crash if neccessary, reload source code for update ...
        cluster.fork();
    });
    server.on('exit', function () {
        //Same as for app, with a different handling of signal...
        server = exec('node server.js');
    });
} else {
    var self = require('app.js');
    self.start();
}

The good thing with cluster is that they are in the same process as the launcher, so I can handle some error without having to restart the app (just calling the right function inside the app for a "soft reboot" of itself), and keep everything in the same process.

While with exec, I m stuck with restarting the server, sometime without knowing what went wrong, and it mean having a subshell, which I dislike.

Is there a way to fork the cluster, but start a different code?

like image 726
DrakaSAN Avatar asked Dec 13 '13 14:12

DrakaSAN


People also ask

Is it possible to cluster multiple node processes?

A cluster module executes the same Node. js process multiple times. Therefore, the first thing you need to do is to identify what portion of the code is for the master process and what portion is for the workers.

Can Nodejs be multithreaded?

Node. js runs JavaScript code in a single thread, which means that your code can only do one task at a time. However, Node. js itself is multithreaded and provides hidden threads through the libuv library, which handles I/O operations like reading files from a disk or network requests.

How do I fix a process out of memory exception in Nodejs?

This exception can be solved by increasing the default memory allocated to our program to the required memory by using the following command. Parameters: SPACE_REQD: Pass the increased memory space (in Megabytes).


2 Answers

My solution to this:

var cluster = require("cluster");
if(cluster.isMaster){
    // Forking Worker1 and Worker2
    var worker1 = cluster.fork({WorkerName: "worker1"});
    var worker2 = cluster.fork({WorkerName: "worker2"});

    // Respawn if one of both exits
    cluster.on("exit", function(worker, code, signal){
        if(worker==worker1) worker1 = cluster.fork({WorkerName: "worker1"});        
        if(worker==worker2) worker2 = cluster.fork({WorkerName: "worker2"});
    });
} else {
    if(process.env.WorkerName=="worker1"){
         // Code of Worker1
    }

    if(process.env.WorkerName=="worker2"){
         // Code of Worker2
    }
}

A more dynamic example:

var cluster = require("cluster");

if(cluster.isMaster){

    // Forking Workers based on args    

    if(process.argv.length < 3){
      console.log("Usage: "+process.argv[1]+" module [module]");
    }

    process.argv.forEach(function (val, index, array) {
      // Don't use this script as worker (index 1 = self)
      if(index>1){
        // Resolve the module before spawning to prevent loop.
        try { require.resolve(val); spawn(val); }
        catch(e) { console.error("Module '"+val+"' not found"); }      
      }
    });

    cluster.on("exit", function(worker, code, signal){
        respawn(worker);
    });
} else {
    var self = require(process.env.WorkerScript);
    self.start();    
}


function spawn(script){
  cluster.fork({WorkerScript: script}).env = {WorkerScript: script};
}

function respawn(worker){
  console.log("Respawning: "+worker.env.WorkerScript)
  cluster.fork(worker.env).env = worker.env;
}
like image 197
Wolfspirit Avatar answered Sep 28 '22 09:09

Wolfspirit


var cluster = require('cluster');

if (cluster.isMaster) {
    var app = cluster.fork(),
        server = cluster.fork();

    app.on('message', function () {
        app.send('app');
    });
    server.on('message', function () {
        server.send('server');
    });
} else {
    process.send('');
    process.on('message', function (code) {
        var self=require('/path/to/' + code + '.js');
        self.start();
    });
}

It work for starting two different cluster, but I m stuck at restarting the app.


EDIT: Final code, with working restart:

var VERSION = 0.3,
    util = require('util'),
    cluster = require('cluster'),
    PATH = process.argv[1].substr(0, process.argv[1].lastIndexOf('/') + 1),
    lib = [],
    childs = [];

function listen(child, i) {
    child.on('message', function(m) {
        if (m.type === 'REBOOT')
        {
            reboot();
        } else if (m.type === 'CODE1') {
            child.send({type: 'START', c: lib[i]});
        } else {
            log('ERROR', '');
        }
    });
    child.on('exit', function(worker, code, signal) {
        delete require.cache[require.resolve(PATH + lib[i])];
        childs[i]=cluster.fork();
        listen(childs[i], i);
    });        
}

function reboot() {
    i = 0;
    do
    {
        childs[i].kill();
        i = i + 1;
    }while (i < childs.length);
}

if (!cluster.isMaster) {
    var self;
    process.send({type:'START'});
    process.on('message', function(m) {
        if (m.type === 'START'){ 
            self = require(PATH + m.c);
            self.start();
        }
    });
} else {
    var i = 3;

    if (process.argv.length < 4)
    {
        log('ERROR', 'Not enought argument');
        log('USAGE', 'node launcher.js x ...');
        log('USAGE', '...: Apps to start (at least one)');
        process.exit(-1);
    } else {    
        do
        {
            lib.push(process.argv[i]);
            i = i + 1;
        }while (i < process.argv.length);

        i = 0;
        do
        {
                childs.push(cluster.fork());
                i = i + 1;
        }while(i < lib.length);

        i = 0;
        do
        {
            listen(childs[i], i);
            i = i + 1;
        }while(i < lib.length);
    }
}

You ll need to store cluster's code in different files and start this code with the paths to the files as arguments.

like image 37
DrakaSAN Avatar answered Sep 28 '22 08:09

DrakaSAN