NodeJS and TCP performance - node.js

I'm testing communication between two NodeJS instances over TCP, using the net module.
Since the TCP doesn't rely on messages (socket.write()), I'm wrapping each message in a string like msg "{ json: 'encoded' }"; in order to handle them individually (otherwise, I'd receive packets with a random number of concatenated messages).
I'm running two NodeJS instances (server and client) on a CentOS 6.5 VirtualBox VM with bridged network and a Core i3-based host machine. The test lies on the client emitting a request to the server and waiting for the response:
Client connects to the server.
Client outputs current timestamp (Date.now()).
Client emits n requests.
Server replies to n requests.
Client increments a counter on every response.
When finished, client outputs the current timestamp.
The code is quite simple:
Server
var net = require('net');
var server = net.createServer(function(socket) {
socket.setNoDelay(true);
socket.on('data', function(packet) {
// Split packet in messages.
var messages = packet.toString('utf-8').match(/msg "[^"]+";/gm);
for (var i in messages) {
// Get message content (msg "{ content: 'json' }";). Actually useless for the test.
//var message = messages[i].match(/"(.*)"/)[1];
// Emit response:
socket.write('msg "PONG";');
}
});
});
server.listen(9999);
Client
var net = require('net');
var WSClient = new net.Socket();
WSClient.setNoDelay(true);
WSClient.connect(9999, 'localhost', function() {
var req = 0;
var res = 0;
console.log('Start:', Date.now());
WSClient.on('data', function(packet) {
var messages = packet.toString("utf-8").match(/msg "[^"]+";/gm);
for (var i in messages) {
// Get message content (msg "{ content: 'json' }";). Actually useless for the test.
//var message = messages[i].match(/"(.*)"/)[1];
res++;
if (res === 1000) console.log('End:', Date.now());
}
});
// Emit requests:
for (req = 0; req <= 1000; req++) WSClient.write('msg "PING";');
});
My results are:
With 1 request: 9 - 24 ms
With 1000 requests: 478 - 512 ms
With 10000 requests: 5021 - 5246 ms
My pings (ICMP) to localhost are between 0.6 - 0.1 seconds. I've not intense network traffic or CPU usage (running SSH, FTP, Apache, Memcached, and Redis).
Is this normal for NodeJS and TCP or it is just my CentOS VM or my low-performance host? Should I move to another platform like Java or a native C/C++ server?
I think that a 15 ms delay (average) per request on localhost is not acceptable for my project.

Wrapping the messages in some text and searching for a Regex match isn't enough.
The net.Server and net.Socket interfaces have a raw TCP stream as an underlying data source. The data event will fire whenever the underlying TCP stream has data available.
The problem is, you don't control the TCP stack. The timing of it firing data events has nothing to do with the logic of your code. So you have no guarantee that the data event that drives your listeners has exactly one, less than one, more than one, or any number and some remainder, of messages being sent. In fact, you can pretty much guarantee that the underlying TCP stack WILL break up your data into chunks. And the listener only fires when a chunk is available. Your current code has no shared state between data events.
You only mention latency, but I expect if you check, you will also find that the count of messages received (on both ends) is not what you expect. That's because any partial messages that make it across will be lost completely. If the TCP stream sends half a message at the end of chunk 1, and the remainder in chunk 2, the split message will be totally dropped.
The easy and robust way is to use a messaging protocol like ØMQ. You will need to use it on both endpoints. It takes care of framing the TCP stream into atomic messages.
If for some reason you will connecting to or receiving traffic from external sources, they will probably use something like a length header. Then what you want to do is create a Transform stream that buffers incoming traffic, and only emits data when the amount identified in the header has arrived.

Have you done any network dump? You may be creating network congestion due to the overhead introduced by enabling 'no delay' socket property. This property will send data down to TCP stack as soon as possible and if you have very small chunks of information it will lead to many TCP packets with small payloads, thus the decreasing transmission efficiency and eventually having TCP pausing the transmission due to congestion. If u want to use 'no delay' for your sockets, try increasing your receiving socket buffer so that data is pulled from the tcp stack faster. Let us know if that helped.

Related

Reuse socket after connect fails in node

I need to reuse socket for two connect calls made using http.request. I tried passing custom agent limiting number of sockets but the first socket is removed before the 2nd connect call is made by code:
https://github.com/nodejs/node/blob/master/lib/_http_client.js#L438
mock code:
var options = {
method: 'CONNECT', agent: new http.Agent({ keepAlive: true, maxSockets: 1 })
};
var request = this.httpModule.request(options);
request.on('connect', (res, sock, head) => {
console.log(sock.address());
// some processing...
var request2 = this.httpModule.request(options);
request2.on('connect', (res, sock, head) => {
console.log(sock.address());
});
request2.end();
});
request.end();
Is there some way by which I can reuse the same socket for two connect calls?
The two unique sockets are required for this form of communication.
Each socket in this case represents a connection between a client and a server. There is no such socket that represents n clients and one server, so to speak. They also don't act like "threads" here, where one socket can perform work for many clients.
By setting the max sockets to 1, you've requested that only 1 client connection be active at any time. When you try to connect that second client, it kills the first one because the max is reached and we need room for a new connection!
If you want to recycle sockets -- For example, a client connects, refreshes the page after an hour, and the same client triggers another connection -- There's probably not a way to do it this high in the technology stack, and it would be far more complicated and unnecessary than destroying the old socket to make way for a new one anyway. If you don't understand why you would or wouldn't need to do this, you don't need to do it.
If you want to send a message to many clients (and you wanted to accomplish it "under one socket" in your question), consider using the broadcast and emit methods.

How to keep a tcp connection always open with node.js

I'm building a tcp-message server with nodejs.
It just sits on my server waiting to accept a connection from lets say an Arduino.
As it, at connection-time, identifies itself with an unique ID (not an IP) I'm able to write data from server > arduino without knowing the IP address of the client-device.
But for that to be efficient, I want the connection to be open as long as possible, preferably as long as the client-device closes the connection. (eg on ip change or something)
This is the (relevant part of) the server:
var net = require('net'),
sockets = {};
var tcp = net.createServer(function(soc){
soc.setKeepAlive(true); //- 1
soc.on('connect', function(data){
soc.setKeepAlive(true); //- 2
});
soc.on('data', function(data) {
//- do stuff with the data, and after identification
// store in sockets{}
})
}).listen(1111);
Is soc.setKeepAlive(true) the right method to keep the connection alive?
If so, what is the right place to put it? In the connect event (1), or right in the callback (2).
If this is not the way to do it, what is?
Your best bet is to periodically send your own heartbeat messages.
Also, you don't need soc.on('connect', ...) because the client socket is already connected when that callback is executed.

How to send Websocket messages with the least amount of latency?

I'm working on a Websocket server programmed in Node.js and I'm planning to send out a multicast message to upwards of 20,000 users. The message will be sent out on a regular interval of a second, however I am worried about the performance from the Node.js server.
I understand that Node.js works asynchronously and creates and destroys threads as it requires, but I am unsure of its efficiency. Ideally I would like to send out the message with an average latency of 5ms.
Currently I'm sending out messages to all users through running through a for loop of all the connected clients as following:
function StartBroadcastMessage()
{
console.log("broadcasting socket data to client...!");
for(var i=0;i < clientsWithEvents.length;i++){ //Runs through all the clients
client = clientsWithEvents[i];
if(client.eventid.toLowerCase() == serverEventName.toLowerCase()) //Checks to see if the Client Event names and server names are the same
client.connection.sendUTF(GetEventSocketFeed(client.eventid)); //Sends out event data to that particular client
}
timeoutId = setTimeout(StartBroadcastMessage,2*1000);
}
Is this an efficient way of sending out a multicast message with a low latency, or is there a better way?
Also, is there an efficient way to perform a load test on the server simulating a number of devices connected to the Websocket server? (So far I have found this Node app https://github.com/qarea/websockets-stress-test)
You can use socket.io to broad cast message.
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.broadcast.emit('user connected');
});
This will avoid latency(iterating all socket objects and formatting message) in sending individual message to client.

Possible to simulate several concurrent connections to test a nodejs app

I have a simple node.js /socket.io (websockets) application running #localhost. I am trying to see how many concurrent connections it can handle. Is it possible to simulate several concurrent users on localhost itself ?
This is my half baked attempt using socket.io-client:
function connectAndSend(){
socket.emit('qand',{
code :'ubuntu'
});
}
socket.on('connect', function () {
});
socket.on('q', function (data) {
console.log(data);
});
function callConnect(){
console.log('calling');
connectAndSend() ;
setTimeout(callConnect,100) ;
}
callConnect() ;
As I see it this only 'emits' a new message every 100 ms and is not simulating concurrent connections.
In your call to connect, you must tell socket.io to create a new connection for each call to connect. For example:
var socket = io.connect(server, { "force new connection": true });
Also, if you want to raise the outbound TCP connection limit (which seems to default to 5 connections per target), do something like
require('http').globalAgent.maxSockets = 1000;
before connecting.
But note that creating and closing tcp sockets at a fast rate will make TCP connections pile up in state TIME_WAIT and depending on your OS and your network settings you'll hit a limit pretty soon, meaning you'll have to wait for those old sockets to timeout before you can establish new connections.
If I recall correctly, the limit was around 16k connections (per target ip/port combo) on Windows (both Server 2008 R2 and Windows 7), and the default TIME_WAIT timeout in Windows is 4 minutes, so if you create more than 16k connections in 4 minutes on Windows, you'll probably hit that wall.
Check here:
Long connections with Node.js, how to reduce memory usage and prevent memory leak? Also related with V8 and webkit-devtools
and specifically - test procedure used by the author of question mentioned above
EDIT:
You can use following tools to check how many requests per second your server is capable of serving
ab - http://httpd.apache.org/docs/2.2/programs/ab.html
siege - http://www.joedog.org/siege-home/

Updating variable in nodejs

I wrote a simple server in node js.
var tls = require("tls"), fs = require("fs"), sys = require("sys");
//Number of messages received
var received=0;
var options = {
key: fs.readFileSync("certs/keys/server.key"),//Server private key
cert:fs.readFileSync("certs/certs/server.crt"),//Server cert.
requestCert: true,//Require client to send it's certificate
rejectUnauthorized:true,
ca:fs.readFileSync("certs/certs/userA.crt") //Root certificate,
};
//Server instance with connection request callback
var server = tls.createServer(options,function(socket){
//Add a listener for receiving data packets
socket.addListener("data", function(data){
received++;
});
}).listen(2195,function(){
console.log("Server started");
});
I also have java client application which makes multiple (300) connections to the server and sends messages. The problem is value of variable "received" does not match with the value of "send" on java side. For. example if I send 100,000 messages from java application, the server shows value of received as 80,000, even though all the messages are successfully received by the server.
I think the issue is variable received is updated by multiple callbacks at the same time and hence the final value is getting messed up. Any idea on how i can get this resolved?
TCP/IP does not guarantee that the number of packets sent matches the number of packets received. So it can happen that two or more consecutive sent packets get "combined" into one. (See -> Nagle's algorithm) or they get split (See -> IP fragmentation) if they dont fit into the MTU.

Resources