Orphaned consumers in disconnected sockets - node.js

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.

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.

Multiple connections with node cluster and socket.io

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)

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.

Client is trying to get data with Socket.io but doesn't seem to connect

I have set up sockets on my client and server, but I can't seem to get my data to come into my client. It seems they are connecting properly, I just can't get any data to come through. There are no error messages either.
Here is my server code:
io.on('connection', function (socket) {
console.log('a user connected');
socket.on('custom-message', function () {
console.log("Hitting messages socket");
Message.find(function(err, messages){
if(err){
socket.emit('custom-message', err)
} else {
socket.emit('custom-message', messages);
}
})
});
});
Here is the function in the client that connects to the socket:
loadMessagesFromServer: function(){
console.log("About to load messages")
socket.on('custom-message', function(msg){
console.log("connected in client", msg)
});
},
Like I said it is a pretty simple example, I just can't seem to get the data in loadMessagesFromServer .. And there are no erros, the only way I have been debugging is trying different things..
You are listening on the event messages. So you need to emit the same event not socket.emit('messages: err', err). Try with socket.emit("messages", error). Moreover, in your server-side code, you need first to receive a message event and only then your socket will emit the messages. Remove the socket.on(custom-messages). Why do you need it?
Server-side code
io.on('connection', function (socket) {
/* Here a client connection is established. The on("connection")
* event will be triggered as many times as`io.connect("the-uri")`
* is triggered (and succeeded) in the client`
*/
// Listening for the post event
socket.on("post", function(messages){
console.log("client posted data:", messages)
// Find messages and emit result
Message.find(function(err, messages){
if(err){
socket.emit('error', err)
} else {
socket.emit("message", messages);
}
});
});
});
Client-side code
registerOnMessageFromServerListener: function(){
socket.on("message", function(msg){
console.log("received message:", msg);
});
registerOnErrorFromServerListener: function(){
socket.on("error", function(error){
console.log("an error occured:", error);
});
registerOnMessageFromServerListener();
registerOnErrorFromServerListener();
socket.emit("post", "a-message");
Also make sure that you call the loadMessagesFromServer before you establish the socket connection

How to disable event listener for socket when user disconnects?

I'm using the Twit library for Node.js, and I begin by creating a stream of incoming tweets here:
var stream = T.stream('statuses/filter', { locations: allLocations })
Then, using Socket.io, I send the tweets to each connected client, and also log when a user disconnects:
io.on('connection', function(socket){
socket.on('disconnect', function() {
console.log("User disconnected!!!!!");
});
stream.on('tweet', function(tweet) {
// sends tweet to client in question:
socket.emit('info', tweet);
});
}
The problem is that even after a user disconnects, the tweets meant for them still keep getting parsed by the event listener, and logged to the console. Is there a way to "disable" all socket-related event listeners when the user disconnects? Or is there a cleaner way to handle this situation with streaming tweets?
Here's one solution:
io.on('connection', function(socket){
socket.on('disconnect', function() {
console.log("User disconnected!!!!!");
stream.removeListener('tweet', onTweet);
});
function onTweet(tweet) {
// sends tweet to client in question:
socket.emit('info', tweet);
}
stream.on('tweet', onTweet);
}
Also, you could just broadcast to all sockets using a single 'tweet' event handler instead of adding a new 'tweet' event handler for every connection.

Resources