Multiple connections with node cluster and socket.io - node.js

im using socket.io with sticky session (https://github.com/indutny/sticky-session). I have a little chat application and always when im using 2 or more sockets on one one machine the socket.on(...) event listener is firing very very slow. When i'm using only one socket-connection for each cluster everything works fine and fast. What could be the cause. Could it be because of sticky session?
Edit:
This is the socket- connect function (of my worker - clusters):
WebSockets.on('connect', function (socket) {
console.log(`${process.pid}`);
//send data example: socket.emit('news', {a: "b"});
//TODO: implement switch case that validates which chat type it is.
//TODO: why is the event so slow when using 2 connections?: https://socketio.slack.com/messages/C02AS4S1H/
socket.on('chat message', function (msg) {
console.log("sending message: " + msg)
try {
//send the message to all other cluster-workers:
process.send({ chat_message: msg });
} catch (e) { }
});
//if the cluster gets a message then he sends it to the user, if its the correct user
function message_handler(msg) {
try {
//TODO: send message only to the correct users
if (msg != null && msg.chat_message != null) {
WebSockets.emit('chat message', msg.chat_message);
}
} catch (e) { }
}
process.on('message', message_handler);
//When the socket disconnects:
socket.on('disconnect', function(reason) {
//remove the event listener:
process.removeListener('message', message_handler);
});
});
Explanation: I'm sending all the data i get from the socket to all the other clusters. Then I catch them with:
process.on('message', message_handler);
To send incomming chat messages.
I created the Clusters with sticky session like in the example (https://github.com/indutny/sticky-session)

Related

Socket sends duplicate messages to client

I am setting up a web socket server with socket.io and it seems like that messages are sent at least twice. Sometimes even trice. (very rarely even 4+ times) They are never sent once though. How should I setup my handlers or my client code so every message is received exactly once all the time?
My client is in swift and my server in node.js. I am running Ubuntu 16.04 on the server itself.
Node.js:
// Here is an array of all connections to the server
var connections = {};
io.sockets.on('connection', newConnection);
function newConnection(socket) {
socket.on('add-user', function(user) {
connections[user.id] = {
"socket": socket.id
};
});
socket.on('chat-message', function(message) {
console.log(message);
if (connections[message.receiver]) {
console.log("Send to: " + connections[message.receiver].socket);
// Here are some varients of the emit command. Seems like they all do the same
//io.sockets.connected[connections[message.receiver].socket].emit("chat-message", message);
//io.to(connections[message.receiver].socket).emit("chat-message", message);
socket.broadcast.to(connections[message.receiver].socket).emit("chat-message", message);
} else {
console.log("Send push notification")
}
});
//Removing the socket on disconnect
socket.on('disconnect', function() {
console.log("The client disconnected");
for (var id in connections) {
if (connections[id].socket === socket.id) {
delete connections[id];
}
}
})
}
The "console.log(message);" in the messages handler is only called once. That's the confusing part for me. If the handler is called twice, why is this only printed once? Still, on the handler in my swift code, the handler for received messages is called multiple times.

Orphaned consumers in disconnected sockets

The stack uses Socket.io, redis and RabbitMQ for a messaging platform.
The problem
A queue to rabbit mq is created when a socket is connected, the consumer cancellation logic is present in on disconnect event. The problem is in a faulty internet setting the disconnect is fired first after the callback of queue creation is executed later thus an orphaned queue is created.
Server code
io.on('connection', async function(socket){
socket.on('initializationAcknowledged', function(){
// Creating a queue on rabbit MQ in auto delete setting
// That is the queue will exists till a consumer is present
// We cancel the consumer in on disconnect event
rabbitMQ.assertQueue('', {autoDelete: true, expires: 6000}, function(err, q) {
if (err) {
console.log('Error while asserting the consumer queue.');
};
// Bind the queue
consumer.bindQueue(q.queue, 'some exchange', socket.userID);
// Creating the consumer to consume on the created queue
consumer.consume(q.queue, function(msg) {
}, {noAck: true}, function(err, obj) {
if (err) {
console.log('There was an errow while assigning a consumer ');
}
// Setting the consumer tag on the socket to cancel consumer.
socket.consumerTag = obj.consumerTag;
});
});
})
// This is to make sure we have initialized and a stable internet connection
socket.emit('initialization')
socket.on('disconnect', function(){
// It fires the disconnet event before and finds the consumerTag as undefined hence does not cancel.
if (socket.consumerTag != undefined) {
consumer.cancel(socket.consumerTag, function(err, ok) {
if (err) {
console.log('Error while cancelling the consumer:', err);
}
if (ok) {
console.log('Consumer cancelled successfully', ok);
}
});
}
});
})
Client code
const socket = io.connect("some url")
socket.on('initialization', function(data){
// This is to make sure we have a stable internet connection
socket.emit('initializationAcknowledged');
}
Why dont you check for the internet connection using dns.lookup(url,callback);
if callback returns true, only then go for the disconnect method.

Node.js emit not working on specific socket.on

I'm trying to send news to my client. On connect, it works great, but for some reason on broadcast2 it wont get any response client sided, even know its the same piece of code, and even that broadcast2's console.log is working.
Q: How can i make sure broadcast2 emit will work?
This works:
socket.on('message', function (data) {
console.log('message gotten');
socket.emit('news', { message: 'xxxx' });
});
this wont work:
socket.on('broadcast2', function (data) {
console.log("broadcast revieced");
socket.emit('news', { message: 'xxxx' });
});
this is node.js response:
total code in node.js
socket.on('message', function (data) {
console.log('message gotten');
});
socket.on('another-message', function (data) {
socket.emit('not-news', { hello: 'world' });
});
socket.on('broadcast2', function (data) {
console.log("broadcast revieced");
socket.emit('news', { message: 'xxxx' });
});
and this on the client side:
var socket = io.connect('mysite:8080');
function sender() {
console.log('sending tester');
socket.emit('sendertester', 2);
}
socket.on('connect',function(){
});
socket.on('tester', function(msg){
console.log("callback");
});
socket.on('news', function(message) {
console.log("INCOMMING NEWS");
console.log(message);
});
UPDATE 1:
The broadcast2 socket, sent by PHP:
function broadcast($message,$broadcast = 'broadcast2') {
$client = new Client(new Version1X('myurlhidden:8080'));
$client->initialize();
$client->emit($broadcast, ['message' => $message]);
$client->close();
}
UPDATE 2:
**Question two: Cause my broadcast2 is sent before the client sided is loaded, and then the client connects to the node, could that be the cause?
But in the same time, im already preloading the class that holds the broadcast2 emitter.
Using codeigniter framework.**
UPDATE 3
I was trying to check my theory on update 2, by having two users logged in, while user one trying to perform the trigger. user two gets no output, so i suppose that theory is busted.
The server cannot send a message to any socket before it is connected. You have to wait until you have something listening to receive what you are sending.
I wouldn't call close after the emit either, as you may close the connection before the client has received the message, emit doesn't wait for the client to receive the data before returning its asynchronous.
Instead let the clients close the connections when they terminate.

Maintain socketio conection to each new tab opened by the same user

I'm making an application for our website where users can send chat messages with each other. I've managed to do this successfully using nodejs and socketio. I have a header with some sort of notification icon just like facebook that can be seen in all the pages. Now if the user opens multiple tabs and he receives a message, then all open tabs should see the icon light up. I've gotten this to work by tracking the socket connections opened by users through a 2D array of sockets:
var container = {};
io.sockets.on( 'connection', function(client) {
client.on('set account online', function(username) {
if (!(username in container)) {
console.log(username + " is now Online" );
container[username] = [];
}
client.username = username;
container[username].push(client);
});
client.on('set account offline', function(username) {
if (username in container) {
delete container[username];
console.log(username + " is now Offline" );
}
});
And then when a message is sent I iterate through the appropriate array element
client.on('send message', function(data) {
if (data.recipient in container) {
var clients = container[data.recipient];
for(var i = 0; i < clients.length;i++){
clients[i].emit('send message', {recipient: data.recipient, message: data.message });
}
}
});
That's working well and all (not sure how well coded it is though). The problem is if the user closes a tab, the socket for that tab still exists in the container variable and node would still try to emit to that socket if a message is received for that particular user. Also it just feels cleaner to un-track any disconnected socket.
I've been thinking about this and I think I have to tie the socket disconnect event to the client side's onbeforeunload event and we all know how that performs across different browsers. Any suggestion regarding what's the proper way to splice off disconnected sockets from the container array?
As per my comment:
You should really be implementing rooms. On each connection each user
should join their own room, any additional connections from the same
user should join this room. You can then emit data to the room and
each client inside it will receive the data.
Your code can be changed to:
io.sockets.on('connection', function(client) {
client.on('set account online', function(username) {
client.join(username);
});
client.on('set account offline', function(username) {
client.leave(username);
});
client.on('send message', function(data) {
io.to(data.recipient).emit('send message', {
recipient: data.recipient,
message: data.message
});
});
});

detect if socket.io emit did not work

How can i detect if a message did not reach my node.js+socket.io server. I have an emit from the server for message sent success.
socket.on('message', function(msg) {
var sendersusername = msg.source;
if (io.sockets.sockets[socket.id] != undefined)
{
io.sockets.sockets[socket.id].emit('messagesentsuccess',
{"user": msg.target,
"message":msg.message
});
}
});
from this code i get a success message when a message reaches the server. How do i know if the message could not be sent to the server?
you can use
socket.on('error', function (err) {
console.log(err);
});
to detect errors`

Resources