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?
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.
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.
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).
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;
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With