Nodejs crashes under stress testing - node.js

I am trying to simulate 1000 websocket connections to a test server.I thought implementing it using cluster.fork, but using it 1000 time will deplete my memory and crash the computer.Async seems to be a solution to the problem,however i still ran out of memory.How am i supposed to conduct such a test while being at the same time careful with my resources?If i use numCpus instead of 500 , i only get 4 connections.
edit :
if (cluster.isMaster) {
//This is the master control process
console.log("Control process running: PID=" + process.pid);
//Fork workers.
for (var i = 0; i < 500; i++) {
worker = cluster.fork();
//Receive messages from this worker and handle them in the master process.
worker.on('message', function(msg) {
console.log('Master ' + process.pid + ' received message from worker ' + msg);
});
}
if (cluster.isWorker) {
console.log('Worker ' + process.pid + ' has started.');
websocketCreation();//function that creates new WebSockets
eventHandler();//function that handles on error,close and messagr
}

Related

Application(Webinterface + Cron-Jobs + HTTP-Clients) -- JAVA vs NODEJS

iam programming a prototype application with the following components:
webinterface for admins
doing cron jobs(statistic generation, ..)
interact with other webservices over http
I started programming with nodejs(typescript) and i got the connection to the other services. Now i got a problem with cron-jobs in nodejs.
Iam using node-cron for executing the cronjob.
Inside one job i need to obtain the status of much pc's and make a summary of it. If i would do this, this would block the main thread.
So i think I need to this in a separate thread.
How can i do this in nodejs? Should i use webworker-threads?
Am I on the proper way?
Should i better use Java(Grails/Spring) for this?
I really like the simplicity of nodejs (for http clients, ..)
Hope someone can give me hope that iam on the proper way.
I will just use Node Cluster. Using cluster, a master can create a multiple workers, which means your cron wont block incoming request. Just make sure that only 1 worker doing the Cron.
I have never working with node-cron before, but I have experience with the SyncedCron. But should be the same.
For the http client there are a lot libraries doing this, you can check Request or httpclient.
Your code should look something like this :
var cluster = require('cluster');
var http = require('http');
var numWorkers = require('os').cpus().length-1; // just give 1 cpu for OS to use, or maybe 2
if (cluster.isMaster) {
console.log('Master cluster setting up ' + numWorkers + ' workers...');
var cronPID=null;
for(var i = 0; i < numWorkers; i++) {
var worker=cluster.fork();
if(i==0){
//instructing the first worker to assume role of SyncedCron.
worker.send('you do it!');
cronPID=worker.process.pid;
console.log("worker "+cronPID+" choosed as Cron worker!");
}
}
cluster.on('online', function(worker) {
console.log('Worker ' + worker.process.pid + ' is online');
});
cluster.on('exit', function(worker, code, signal) {
// have to revive the worker
console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);
console.log('Starting a new worker');
var newWorker=cluster.fork();
if(cronPID==worker.process.pid)
{ // need to re-elect a new cron worker!
newWorker.send('you do it!');
cronPID=newWorker.process.pid;
console.log("worker "+cronPID+" choosed as Cron worker!");
}
});
}else
{ // worker sides
process.on('message', (msg) => {
// validate your message that you get
// if validated, create a cron job here
});
// create your express below, I assume you use express instead of node's http library
var express = require('express');
var app = express();
app.post...........
}
Note :
To revive the master, use something like "forever"
Your server should have multiple core, at least 4 but I recommend more (8 maybe?).

Restart a cluster worker in nodejs not listening for connections

I have a use case where I need to dedicate one worker to listen for incoming messages from an AWS sqs queue and process them, the rest of the workers are listening on the desired port for connection. Following is the code
var cluster = require('cluster');
if (cluster.isMaster) {
/** Master process */
/**
* spawn a worker for each available cpu
* HACK: Downsizing worker count will not restore tasks that were handled by those extra workers
*/
var cpuCount = require('os').cpus().length;
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
/** re-spawn worker if dead */
cluster.on('exit', function(worker) {
console.log('worker-' + worker.id + ' died');
cluster.fork();
});
} else if (cluster.worker.id == 1) {
// Check for incoming messages from an AWS SQS queue
} else {
//Listen on the port for connections
}
If the worker listening for connection on the port dies, it is revived. But, I cannot figure out the solution of how to revive the worker which is listening for incoming messages from the SQS queue if it dies due to some error.

When using http.createServer with clusters, why is callback provided to http.createServer being called more than once?

I was trying to understand how http requests are served by workers of a cluster. I started with the code at http://nodejs.org/api/cluster.html and added some logging around it.
I put the following code in server.js and executed the file with:
NODE_DEBUG=cluster node server.js
var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log('number of CPUs: ' + numCPUs);
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
console.log('forking worker ' + i);
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
console.log('worker spawned: ' + cluster.worker.process.pid);
var someVar = {'b': 1};
// Workers can share any TCP connection
// In this case its a HTTP server
http.createServer(function(req, res) {
console.log('someVar.b current value: ' + someVar.b + ". someVar.b will be incremented");
someVar.b = someVar.b + 1;
console.log('Request being processed by worker: ' + cluster.worker.process.pid);
res.writeHead(200);
res.end("hello world\n");
console.log('response ended by worker: ' + cluster.worker.process.pid);
}).listen(8000);
}
I then went to localhost:8000 once, and lo and behold, "hello world" was the response as I expected. What I didn't expect is some log statements being printed twice:
number of CPUs: 2
forking worker 0
forking worker 1
535,Master Worker 536 online
535,Master Worker 537 online
worker spawned: 536
worker spawned: 537
someVar.b current value: 1. someVar.b will be incremented
Request being processed by worker: 536
response ended by worker: 536
someVar.b current value: 2. someVar.b will be incremented
Request being processed by worker: 536
response ended by worker: 536
Specifically, I expected that for one request to my server, the following lines would only be printed once:
someVar.b current value: X. someVar.b will be incremented
Request being processed by worker: XXX
response ended by worker: XXX
Instead my functions are getting executed twice by the same worker. Could someone please shed some light on this?
Did you check req.url? I bet one of the two requests is for /favicon.ico.

Node.js Performance using Cluster

I have been trying to figure this out for a while. I wrote a very simple http server in node to benchmark the effect of using cluster. Here is my code:
var cluster = require('cluster');
var http = require('http');
var numCPUs = 0; //require('os').cpus().length;
if(process.argv.length >= 3)
{
numCPUs = process.argv[2];
}
if (cluster.isMaster && numCPUs > 0) {
console.log("launching " + numCPUs + " procs");
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
console.log("launching proc #" + i);
cluster.fork();
}
cluster.on('death', function(worker) {
console.log('worker ' + worker.pid + ' died');
});
} else {
// Worker processes have a http server.
http.Server(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(3000);
}
The problem is that I am not seeing any performance gain at all. 1 process has better performance most of the time. And, If I add more work, like retrieving data from redis or mongo then increasing the processes helps, but only modestly (around 15%). I've tried this on an i7 MBPr (quad-core with HT), and an i5 (quad-core) Win7 systems both with the same results.
Can someone please explain whats wrong with this code? Or, why am I not seeing an advantage/benefit in using cluster?
Your test appears to be almost purely I/O-oriented and in that situation using cluster provides little benefit (as you've seen) because I/O is concurrent regardless.
To see a significant benefit you'd need to have portions of your code that are CPU-bound because it's only then that you can provide additional parallelism between your cluster workers.

How to gracefully restart a NodeJS server?

Currently, my prod environment for a side project is a git repo, where I pull in some code, manually kill the server with Ctrl-C, and restart it manually.
I realize there are a lot of things wrong with this. For instance, what if a user is still in the middle of doing something important and the process is crunching sensitive data, and I just killed it?!
When I used node v0.4.x there was a nice Cluster module that could restart the server gracefully, when the application is in a quiet state. In v0.6.x the Cluster module is built into node, but it's really, really bare, and doesn't have the graceful restart ability.
Anyone know how I can gracefully restart a nodejs server in v0.6.x?
You can handle POSIX signals in node code.
See in the example code, that will handle SIGINT (Ctrl-C for instance) as a STOP signal for all cluster workers, and SIGUSR2 will just restart all workers
So, issuing kill -SIGUSR2 PID, where PID is node master PID will restart all cluster
module.exports = function(app) {
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
var workerList = new Array();
var sigkill = false;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
var env = process.env;
var worker = cluster.fork(env);
workerList.push(worker);
}
process.on('SIGUSR2',function(){
console.log("Received SIGUSR2 from system");
console.log("There are " + workerList.length + " workers running");
workerList.forEach(function(worker){
console.log("Sending STOP message to worker PID=" + worker.pid);
worker.send({cmd: "stop"});
});
});
process.on('SIGINT',function(){
sigkill = true;
process.exit();
});
cluster.on('death', function(worker) {
if (sigkill) {
logger.warn("SIGKINT received - not respawning workers");
return;
}
var newWorker = cluster.fork();
console.log('Worker ' + worker.pid + ' died and it will be re-spawned');
removeWorkerFromListByPID(worker.pid);
workerList.push(newWorker);
});
} else {
process.on('message', function(msg) {
if (msg.cmd && msg.cmd == 'stop') {
console.log("Received STOP signal from master");
app.close();
process.exit();
}
});
app.listen(3000);
}
function removeWorkerFromListByPID(pid) {
var counter = -1;
workerList.forEach(function(worker){
++counter;
if (worker.pid === pid) {
workerList.splice(counter, 1);
}
});
}
}
There's a module named Forever.
That can gracefully restart the process. I suppose then you can somehow run several instances with cluster (one on each core) and use Forever to monitor / restart them.
This is just an option I found; I'm open to suggestions!
There's also a module named PM2. It has the ability to stop all processes in a cluster.

Resources