How to share a variable among clusters in nodejs? - javascript

I am running my express application with clusters.
My master has some kind of caching with a variable that needs to be shared across my workers.
Can i do this in the following way as using a physical datastore is not feasible for me ?
if (cluster.isMaster) {
// Create a worker for each CPU
global.my_cache_variable = 'xyz';
console.log("Number of cpu cores", cCPUs);
for (var i = 0; i < cCPUs; i++) {
cluster.fork();
}
cluster.on('online', function (worker) {
console.log('Worker ' + worker.process.pid + ' is online.');
});
cluster.on('exit', function (worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died.');
cluster.fork();
});
} else {
//create server code here
// and access the global cache variable with the workers
}

Although you can do inter-process communication, I it's not easy and may not be what you want (sharing variable across processes).
Your best option is probably to externalize your cache store, e.g. a Redis store that all clusters can access. It can be on the same machine or another one. It will also help if you need to scale horizontally, as it will be shared by multiple machines.

Related

Possible Node.js memory leak when using callbacks

I've been coding with Node.js, but since I'm not that familiar with how GC works with async methods, I believe I created a memory leak in this code:
function getPriceCC(coins, chn) {
// Get the spot price of the pair and send it to general
cc.priceFull(coins.map(function(c){return c.toUpperCase();}),['USD', 'EUR']).
then(prices => {
var msg = '__**CryptoCompare**__\n';
for(var i = 0; i < coins.length; i++)
msg += ('- **' + coins[i].toUpperCase() + '-USD** is : `' +
prices[coins[i].toUpperCase()]['USD']['PRICE'] + ' USD` (`' +
Math.round(prices[coins[i].toUpperCase()]['USD']['CHANGEPCT24HOUR']*100)/100 + '%`).\n'
);
chn.send(msg);
delete chn;
delete coins;
delete msg;
delete prices;
})
.catch(console.error);
}
I read about closure, so I tried deleting the variables inside the callback function, but it keeps stacking up. I've also found that some people dislike the use of var and say my code is horrible, while some example code from Google uses var.
If any clarification is needed or extra code required, please do tell.
Note: Node version is 8.0.0, and the instance is Ubuntu 16.04 on AWS.

node js clustering is repeating the same task on all 8 processes

I've been trying to enable clustering in my node js app. Currently I use this snippet to enable it:
var cluster = require('cluster');
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 dying workers
cluster.on('exit', function () {
cluster.fork();
});
}
And basically my code performs writes to a Firebase database based on conditions. The problem is that the writes are occurring 8 times each, rather than one worker just taking care of one write task, it seems that all threads are performing all tasks. Is there a way to avoid this? If so, can someone point me in the direction of some resources on this? I can't find anything on google for using Firebase with node js clustering. Here is an example of the way one of my functions work (ref is my firebase reference):
ref.child('user-sent').on('child_added', function(snapshot) {
var message = snapshot.child('message');
payload['user-received/'] = message;
ref.update(payload); // this occurs once for each fork so it updates 8 times
});
If you're spawning 8 threads and each thread attaches a listener on the same location (user-sent), then each thread will fire the child_added event for each child under that location. This is the expected behavior.
If you want to implement a worker queue, where each node under user-sent is only handled by one thread, you'll have to use a work-distribution mechanism that ensures only one thread can claim each node.
The firebase-queue library implements such a work claim mechanism, using Firebase Database transactions. It's been used to scale to a small to medium number of workers (think < 10, not dozens).

Load testing node.js app on Amazon EC2 instance

I am trying to load test my node.js application with endpoint as API hosted on an m4.large instance using JMeter with 1 master and 3 slaves. The 'server.js' file uses clustering in node.js as follows:
var C_NUM_CPU = 2;
// Listen for dying workers
if (cluster.isMaster) {
for (var i =0; i < C_NUM_CPU; i ++)
{
cluster.fork();
}
cluster.on('exit', function (worker) {
// Replace the dead worker
console.log('Worker %d died :(', worker.id);
cluster.fork();
});
return;
}
When I tested keeping the 'var C_NUM_CPU=2', the max response time crossed 42s, however, on making it 6, the response time dropped to 1.7s! vCPU for m4.large is just 2, then how is the load being handled? Also, in such a case, how to determine the most optimal choice of an instance?
The issue was JMeter slaves. They were dying due to increased response time. Solved on increasing the number of slaves.

Streaming images with Nodejs on embedded device

I'm trying to stream images from a Node.js server to a client site. I've started with socket.io, but my implementation is fairly memory intensive (and possibly leaking as I'm not fluent in JavaScript). I'm just prototyping at this point with 10 sample images:
Server Side
Responds to a socket.io trigger with the following function that "streams" 10 images to the client at roughly 100ms intervals.
socket.on('img_trigger', function(data) {
var img_num = 0;
var timeoutHandle = null;
function startTimeout() {
stopTimeout();
if (img_num < 10) {
timeoutHandle = setTimeout(updateStream, 100);
}
}
function stopTimeout() {
clearTimeout(timeoutHandle);
}
function updateStream() {
var file = './sampleframes/sample-' + img_num + '.png';
fs.readFile(file , function(err, file_buff) {
if (err !== null) {
console.log('readFile error: ' + err);
} else {
socket.emit('img_stream', { buffer: file_buff });
}
file_buff = null;
++img_num;
});
startTimeout();
}
// kicks off first image
startTimeout();
});
Client Side
Capture the raw buffer data and generate a PNG with an <img> element.
socket.on('img_stream', function(data) {
var img_data = arrayBufferToBase64(data.buffer);
var panel = $('#frame-panel');
$('#frame').html('<img src="data:image/png;base64,' +
img_data + '" width="' + panel.width() + '" height="' +
panel.height() + '" />');
});
If I trigger the server once, it works fine but not great. I notice the memory usage go up significantly, and it crashes after several triggers. Can I improve my code here to be efficient or should I try a new approach?
I've looked into using Node's File Streams, socket.io-streams, and even Binary.js (though I hesitate to require our clients to have too-modern of browsers) and they look promising, but I don't quite know which would be best for my use-case. Any help or guidance would be greatly appreciated.
The web interface I'm developing is for an FPGA (Zynq-7000) based camera running PetaLinux with Node.js cross-compiled for the ARM processor, so I don't have a lot of server-side resources to work with. As such, I'd like to have the client-side do as much of the processing as possible. Eventually, streaming video would be incredible, but I'd be satisfied with reading and displaying successive frames at a reasonable rate.
This may be due to a memory leak within the socket.io library (see here for a description, and here for a proposed fix).
To fix this, download and use the latest version of socket.io from here.

Reading from and writing to multiple ip's with node.js

I've been through a tutorial with Node.js, but it seems everything was aimed at what to do with it from the server side. I was playing around with it to see how it would work writing to multiple devices on our network with non-blocking i/o. Here's what I have so far (rough code, sorry):
var net = require('net');
var nodes = [<list of IP addresses>];
function connectToServer(ip) {
conn = net.createConnection(3083, ip);
console.log ("Creating connection to: " + ip + "\n");
conn.on('connect', function() {
conn.write ("act-user::myuser:ex1::mypass;");
});
conn.on('data', function(data) {
var read = data.toString();
if (read.match('ex1'))
{
console.log(ip + ": " + read);
}
});
conn.on('end', function() {
});
}
nodes.forEach(function(node) {
connectToServer(node);
});
I see it go through the list and kicking off a connection to each IP address, but then it seems to write the login line to the same host over and over again. I think somewhere I'm getting my streams crossed (just like Egon said to never do), but I'm not sure how to fix it. The non-blocking node.js world is still kind of difficult for me to think through.
Your issue is a JavaScript kink. In JavaScript, if a variable is defined without var before it, it will be set as global. This is happening to the conn variable in your connectToServer() function. conn is being overwritten and is not reset after the connectToServer scope is exited.
Try adding var in front of the following.
conn = net.createConnection(3083, ip);
Result
var conn = net.createConnection(3083, ip);

Categories

Resources