How to verify Cluster working in node js? - node.js

I am new to nodejs and currently playing with its features, one of the important feature I came across is Cluster, I tried to implement that for my sample application using expressjs, angular and nodejs.
Cluster code:
var cluster = require('cluster');
if (cluster.isMaster) {
var cpuCount = require('os').cpus().length;
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
} else {
var express = require('express');
var app = express();
var exportRouter=require('./routers/exportRouter');
var process = require('process');
fakeDB = [];
app.use(express.static(__dirname + '/public'));
app.use(require('./routers/exportRouter.js'));
console.log('process Id :',process.pid);
app.listen(3000, function(){
console.log('running on 30000');
});
}
I have added following code in my routers to block the event loop,so when I make first request It will block one nodejs worker. so if another user makes call while first node is blocked second worker should pick that up.
router code :
var express = require('express');
var exportRouter = express.Router();
var process = require('process');
exportRouter.get('/getMe',function(req,res){
console.log('I am using process ',process.pid);
console.log('get is called');
fakeDB.push(req.query.newName+' '+ process.pid);
res.send(req.query.newName + ' ' + process.pid);
console.log('New name received ',fakeDB);
console.log('New name received ',fakeDB);
var d = new Date().getTime();
console.log('old ',d)
var x = d+10000;
console.log('should stop post ',x);
while(true){
var a = new Date().getTime();
//console.log('new ',a)
if(x<a){
break;
}
}
console.log('I am releasing event loop for ',process.pid);
});
module.exports = exportRouter;
it does not serve other request using another worker and waits for blocked node worker.. BTW I am using node js version 0.12.7(64bit) and 4 cpus.
THanks in advance..

it does not serve other request using another worker and waits for blocked node worker
Your testing methodology is probably wrong. Here's a simplified version of your sample.
var cluster = require('cluster')
if (cluster.isMaster) {
var cpuCount = require('os').cpus().length
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork()
}
} else {
var express = require('express')
var app = express()
console.log('process Id:', process.pid)
app.get('/', function (req, res) {
console.log('pid', process.pid, 'handler start, blocking CPU')
var i = 0;
while (i < 10e9) {
i++
}
console.log('pid', process.pid, 'unblocked, responding')
res.send('thanks')
})
app.listen(3003, function () {
console.log('running on 3003')
})
}
If I run this in one terminal, then open two other terminals and as quickly as possible fire off a curl localhost:3003 in each terminal, I can see the second request arrives and begins processing before the first request gets a response:
pid 53434 handler start, blocking CPU
pid 53437 handler start, blocking CPU
pid 53434 unblocked, responding
pid 53437 unblocked, responding

Related

Running socketio and nodejs cluster module

Server
var cluster = require('cluster');
// Code to run if we're in the master process
if (cluster.isMaster) {
// Count the machine's CPUs
var cpuCount = require('os').cpus().length;
// Create a worker for each CPU
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
// Listen for terminating workers
cluster.on('exit', function (worker) {
// Replace the terminated workers
console.log('Worker ' + worker.id + ' died :(');
cluster.fork();
});
// Code to run if we're in a worker process
}
else {
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);
const port = process.env.PORT || 9090;
server.listen(port,() => {
console.log('Server running at http://127.0.0.1:' + port + '/');
});
io.on('connection', function (socket) {
console.log("CONNECTED")
});
}
Client
import io from 'socket.io-client'
const socket = io('http://localhost:9090');
socket.on('notification', (data) => {
if(props.user && data.user._id === props.user._id) {
this.setNotification(data.notification);
}
})
error message
http://localhost:9090/socket.io/?EIO=3&transport=polling&t=MaDRz1u&sid=VzBUqt22usNbdqKCAAAb 400 (Bad Request)
When i remove the if else and keep the code that is in the else statement everything works. What do I need to add so the sessionID is not unknown.
The response object is {"code":1,"message":"Session ID unknown"}
This happens because each child process created by clusters are not in sync and do not know of each other. To overcome this you will need an adapter to communicate between the clusters.
Refer the Socket.io documentation to overcome this issue.

Can't kill a specific worker in Node JS cluster

I'm using node + express as my web server, and has a cluster of 4 workers.
I tried several ways to deliberately kill a worker:
process.exit() in a controller, and triggered using a browser action. Thought this is just for a single worker process, but turned out all workers were killed.
Again in a controller, I let a worker send suicide announcement to the master:
process.send('suicide');
and here goes my master process:
if (cluster.isMaster) {
console.log(`Master cluster setting up ${numWorkers} workers...`);
for (let i = 0; i < numWorkers; i++) {
const worker = cluster.fork();
worker.on('message', msg => {
console.log(worker.process.pid + ' wants to suicide');
worker.kill();
process.kill(worker.process.pid);
});
}
}
It turned out, worker.kill() doesn't affect at all, and process.kill(worker.process.pid); killing all 4 workers again. Also, the console.log appeared 4 times which I don't understand. I used a browser to trigger some action that hence triggers the suicide announcement, shouldn't this be a single worker's behavior?
I'm also using WebSockets in the projects and keeps a connection, don't know if this matters. Any help is appreciated!
EDIT:
Thanks for #Mia I found the reason: when I put process.exit() in the else statement(when cluster.isWorker) it works fine, but when put in a specific controller, it turns out to affect all the workers. Don't know how to solve yet. Shouldn't the controller affect only one specific worker?
I am sorry to write here, I am unable to comment due to my reputation.
I have written like the following and it works fine.
first clusters are created and open servers, whenever a cluster gets a request app.get("/"), 2 seconds later it exits by process.exit method.
you can confirm that this works well by printing out remaining workers.
'use strict';
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const cpus = os.cpus().length;
for (let i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code) => {
console.log(`${worker.process.pid} is killed`);
console.log("remaining wokers");
const workers = Object.keys(cluster.workers);
for(let worker of workers){
console.log(cluster.workers[worker].process.pid);
}
});
} else {
console.log("process id ",process.pid);
const express = require("express");
const app = express();
const path = require("path");
const fs = require("fs");
const http = require('http').Server(app);
const io = require('socket.io')(http);
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(express.static('public'));
app.get('/',function(req,res){
console.log("connected via" , process.pid);
setTimeout(()=>{
process.exit();
},2000)
res.sendFile(path.join(__dirname+"/public/view/index.html"));
});
http.listen(3000);
}
----------------------------code separated into modules---------------
Root (server.js)
'use strict';
const cluster = require('cluster');
const os = require('os');
if (cluster.isMaster) {
const cpus = os.cpus().length;
for (let i = 0; i < cpus; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code) => {
console.log(`${worker.process.pid} is killed`);
console.log("remaining wokers");
const workers = Object.keys(cluster.workers);
for(let worker of workers){
console.log(cluster.workers[worker].process.pid);
}
});
} else {
console.log("process id ",process.pid);
const express = require('./modules/express');
const http = require('http').Server(express);
http.listen(3000);
}
Express
const express = require("express");
const app = express();
const bodyParser = require('body-parser');
const route = require('../route');
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(express.static('public'));
route(app);
module.exports = app;
Route (index.js)
const HomeController = require('../controllers/HomeController');
module.exports = function(app){
app.get('/',HomeController.renderIndex);
app.get('/killProcess',HomeController.killProcess);
}
HomeController
const path = require('path');
exports.renderIndex = function(req,res){
console.log("connected via" , process.pid);
res.sendFile(path.join(__dirname+"/../public/view/index.html"));
}
exports.killProcess = function(req,res){
res.write(`current process ${process.pid} is killed`);
res.end();
process.kill(process.pid);
}
Client side (browser)
$("#kill").on("click",function(e){
$.ajax({
url:"/killProcess"
}).success(function(data){
console.log(data);
})
})

Socket.io events working only in the same process

I'm trying to use socket.io & sticky-session to pass messages to my clients.
The problem is that client which connect to one of the processes won't get messages from other processes, only from the process he is connected to.
How can I make web sockets to work across all processes?
Server.js:
var cluster = require('cluster');
var app = require('./config/express')(db);
// Init the server to run according to server CPU's
if (cluster.isMaster) {
for (var i = 0; i < 4; i++) {
cluster.fork();
}
} else {
app.listen(config.port, function () {
console.log('Process ' + process.pid + ' is listening to all incoming requests');
});
}
Process.js:
var http = require('http');
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var ns = io.of('/ns');
var sticky = require('sticky-session');
if (!sticky.listen(server, 8080)) {
// Master code
server.once('listening', function() {
console.log('server started on 8080 port');
});
}
client.js:
var io = require('socket.io-client');
var serverUrl = 'http://localhost:8080/ns';
var conn = io.connect(serverUrl);
conn.on('malware', function(infectedProcess){
console.log('infectedProcess: ' + infectedProcess);
});

Node.js Cluster + Express always invoke the same worker

I'm trying to use the cluster module to handle multiple http requests concurrently with Express.
With the code below I'm able to spawn multiple workers and have all of them listen on the same port. The large for loop is there to simulate heavy load on the web server.
What I'd like to see is that if a worker is busy processing one http request when a second request comes in, a different worker will get invoked and handle that second request. Instead, when I try to issue multiple requests using curl, all requests are processed sequentially by one single worker; no other workers are ever invoked even though they've been forked.
Could it be that I'm using Express incorrectly? Thanks in advance!
var cluster = require('cluster');
if (cluster.isMaster) {
var cpuCount = require('os').cpus().length;
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
}
else {
var http = require('http'),
app = require('express')();
http.createServer(app).listen(31415, function () {
console.log(process.pid + " listening on 31415");
});
app.get('/', function (req, res) {
var t= 0;
for(var i=0; i < 100000000; i++){
t++;
}
res.send('done');
});
}
Try not to use built-in module ?
master.js
var cp = require('child_process');
var net = require('net');
// create tcp server listen to a port
var tcp = net.createServer();
tcp.listen(8000, function(){
// detect cpu number, and fork child process
for (var i=0;i< require('os').cpus().length; i++) {
var worker = cp.fork('child.js');
worker.send(i, tcp._handle);
}
tcp.close();
});
child.js
var express = require('express');
var app = express();
process.on('message', function(id, handle){
app.get('/',function(){
console.log(process.pid+' is listening ...');
});
app.listen(handle, function(){
console.log(process.pid + 'started');
});
});
this works fine with express 3.x

NodeJS|Socket.IO how to send server handler to workers?

The idea is to create server in master process, and handle requests in workers. I want to utilize all CPU cores and to have kinda load balance as well.
At first I tried to send server handler from master to worker:
var cluster = require('cluster');
if (cluster.isMaster) {
var app = require('express').createServer();
app.listen(1234);
var worker = cluster.fork();
worker.stdin.write('fd', 'utf8', app._handle);
} else {
process.stdin.resume();
process.stdin.on('fd', function(fd){
var stream = require('net').Stream(fd);
var io = require('socket.io').listen(stream);
io.sockets.on('connection', function(socket){
...
}
}
}
but write did not fired on('fd'...) event handler in worker. Then I put everything to master in order to check if this is possible at all:
var app = require('express').createServer();
app.listen(1234);
var stream = require('net').Stream(app._handler);
var io = require('socket.io').listen(stream);
the server starts without any errors but does not work. I cant event request the socket.io.js script from the client side with tag:
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
Response: Cannot GET /socket.io/socket.io.js
So I have two troubles:
How to send opened socket descriptor to the worker?
How to set up handler for the server by this descriptor?
You don't need to take care of file descriptors etc on your own, look at a similar question I've just answered here:
Node.js, multi-threading and Socket.io
Code sample:
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
var sio = require('socket.io')
, RedisStore = sio.RedisStore
, io = sio.listen(8080, options);
// Somehow pass this information to the workers
io.set('store', new RedisStore);
// Do the work here
io.sockets.on('connection', function (socket) {
socket.on('chat', function (data) {
socket.broadcast.emit('chat', data);
})
});
}

Resources