Redis connections increasing from nodejs - node.js

I am having two connections from nodejs to Redis:
to set a list variable
to subscribe to a channel
The longer the socket is running, the more connections I get.
I have tried to set timeout values in redis, tried to close redis connections on close of socket.io
var Redis = require('ioredis');
var globalRedis = new Redis({
port: process.env.REDIS_PORT, // Redis port
host: process.env.REDIS_HOST, // Redis host
family: 4, // 4(IPv4) or 6(IPv6)
db: 1
});
[...]
io.sockets
.on('connection', socketioJwt.authorize({
secret: process.env.JWT_SECRET,
timeout: 15000 // 15 seconds to send the authentication message
}))
.on('authenticated', function(socket) {
var redis = new Redis({
port: process.env.REDIS_PORT,
host: process.env.REDIS_HOST,
// password: process.env.REDIS_PASSWORD,
family: 4,
db: 1
});
socket.on('disconnect', function() {
redis.unsubscribe('channel-'+socket.decoded_token.uid);
});
socket.on('error', function() {
redis.unsubscribe('channel-'+socket.decoded_token.uid);
});
redis.subscribe('channel-'+socket.decoded_token.uid);
redis.on('message', function(channel, message) {
message = JSON.parse(message);
socket.emit('message', message);
});
});
function disconnectUser(userObject){
globalRedis.sismember('online_uids', userObject.uid).then(function(){
globalRedis.srem('online_users', userObject.nickname).then(console.log(userObject.nickname + ' disconnected!'));
globalRedis.srem('online_uids', userObject.uid);
});
}
const port = process.env.SOCKET_PORT;
const host = process.env.SOCKET_URL;
const logger = () => console.log(`Listening: http://${host}:${port}`);
http.listen(port, host, logger);
So the connection in globalRedis is always active and no problem whatsoever.
But as time goes by the subscribed connections in redis increase, even if I unsubscribe on disconnect and on error.
Any ideas?
P.S.: sorry for my bad code and knowledge, this is my first nodejs ever.

You are creating a new redis client for every authenticated socket connection and then it is subscribing to the channel. Unsubscribing from the channel doesn't close the connection also timeout only applies to normal clients and it does not apply to Pub/Sub clients.
Close redis client connection on socket disconnect or error.
redis.quit()
Reference Redis Docs

So just in case anyone stumbles upon the same issue. I ended up putting each user into his own channel:
io.sockets
.on('connection', socketioJwt.authorize({
secret: process.env.JWT_SECRET,
timeout: 15000 // 15 seconds to send the authentication message
}))
.on('authenticated', function(socket) {
socket.join(socket.decoded_token.uid);
connectUser(socket.decoded_token);
socket.on('disconnect', function() {
disconnectUser(socket.decoded_token);
});
socket.on('error', function() {
disconnectUser(socket.decoded_token);
});
socket.on('connect_failed', function() {
disconnectUser(socket.decoded_token);
});
socket.on('reconnect_failed', function() {
disconnectUser(socket.decoded_token);
});
socket.on('message', function(message){
socket.emit('message', message);
});
});
redis.on('message', function(channel, msg) {
// console.log(msg);
try{
message = JSON.parse(msg);
channels = message.data.socketchannel
channels.forEach(recipient => {
io.sockets.in(recipient).emit('message', message);
})
}
catch(e) {
const fs = require('fs');
fs.writeFile("./log/errorlog"+ new Date().toJSON().slice(0,10), e, function(err) {
if(err) {
return console.log(err);
}
})
}
});

Related

Socket.io only emits on server restart in nodejs

I am using socket.io in nodejs for chat system, and I am able to send the message on server in socket.js file in nodejs. But when I emit that message to other users from socket in nodejs. Then this message is not receiving on the client side.
Here is my nodejs code:
var users = [];
const io = require('socket.io')(server, {
pingInterval: 5000,
pingTimeout: 5000
});
io.on('connection', (socket) => {
console.log("User connected: ", socket.id);
socket.on("user_connected", function (id) {
users[id] = socket.id;
io.emit("user_connected", id);
});
socket.on('message', (msg) => {
var socketId = users[msg.friendId];
socket.to(socketId).emit('new_message', msg);
let chat = new ChatData({
user: msg.userId,
friend: msg.friendId,
message: msg.message
});
chat.save().then(() => {
console.log('message saved');
}).catch((err) => {
console.log('error is ', err);
});
});
});
Here is my client side code:
const socket = io("ws://localhost:3000");
socket.on('connect', function() {
socket.emit("user_connected", userId);
});
function sendMessage(message) {
let msg = {
userId: userId,
friendId: friendId,
message: message.trim()
}
// Send to server
socket.emit('message', msg);
}
// Recieve messages
socket.on('new_message', (msg) => {
console.log(msg)
});
Client side socket js library https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.1.2/socket.io.js

Connection socket.io incremented after stopping the server

I have a problem with socket.io from nodeJs.
I am trying to create a connection from a client to a server. Once the client is connected, the server sends a message to the client (ack).
Until then, everything works fine but when I disconnect the server and restart it, it sends me the message twice to the client.
If I repeat the manipulation a third time, three messages will appear.
I have captured the problem:
client.js
var socket = require('socket.io-client')('http://localhost:8050', {
'forceNew':true
});
socket.on('connect', onConnect);
function onConnect(){
console.log('connect ' + socket.id);
socket.emit('sendConnect','First connect');
socket.on("ack", function(data) {
console.log("ack reçu");
});
socket.on('disconnect', function(reason) {
console.log(reason);
});
}
server.js
var io = require("socket.io");
var sockets = io.listen(8050);
sockets.on('connection', function (socket) {
socket.on('sendConnect', function (data) {
console.log("message :" + data);
socket.emit('ack');
socket.on('disconnect', function() {
console.log('Got disconnect!');
});
});
});
I looked if this bug was already arriving without finding an answer.
I must surely be doing something wrong!
Thank you in advance for your help.
Your onConnect function adds new event listeners each time the socket connects. Move the event subscriptions out of onConnect like this:
var socket = require('socket.io-client')('http://localhost:8050', {
forceNew: true
});
socket.on('connect', onConnect);
socket.on('ack', function(data) {
console.log('ack reçu');
});
socket.on('disconnect', function(reason) {
console.log(reason);
});
function onConnect() {
console.log('connect ' + socket.id);
socket.emit('sendConnect', 'First connect');
}

Wait/reconnect until TCP client connection can be established?

Here is an example of how to create a TCP client connection from the node net docs (https://nodejs.org/api/net.html#net_net_connect_options_connectlistener)
const client = net.createConnection({ port: 1905 }, () => {
// 'connect' listener
console.log('connected to server!');
client.write('world!\r\n');
});
client.on('data', (data) => {
console.log(data.toString());
client.end();
});
client.on('end', () => {
console.log('disconnected from server');
});
If the server is not available I get Error: connect ECONNREFUSED 127.0.0.1:1905.
What would be a good way to wait/reconnect until the server is available and connect when it is, instead of throwing an error?
EDIT: Here is an alternative approach I have tried, but here I get the problem
MaxListenersExceededWarning: Possible EventEmitter memory leak
detected. 11 connect listeners added. Use emitter.setMaxListeners() to
increase limit
I would like the latest listener to replace earlier listeners. They all listen for the same thing. I just want to retry.
function initTcpClient() {
console.log("Initiating TCP client...")
var tcpSocket = new net.Socket();
const client = net.createConnection({ port: 1905 }, () => {
tcpSocket.on('error', function onError(err) {
setTimeout(connect, 1000);
});
connect();
function connect() {
console.log("Looking for TCP server...");
tcpSocket.connect(argv.tcpport, argv.tcphost, function onConnected() {
console.log("Connecting to TCP server...");
tcpSocket.on('data', function onIncoming(data) {
if (connectedWebsocketClient) {
console.log('Forwarding to WebSocket: %s', data);
webSocketClient.send(data.toString());
} else {
console.log('Not connected to websocket client. Dropping incoming TCP message: %s', data);
}
});
tcpSocket.on('close', function onClose(hadError) {
console.log("Connection to TCP server was closed.");
connectedToTcpServer = false;
setTimeout(connect, 1000);
});
console.log("Connected to TCP server.");
connectedToTcpServer = true;
});
}
}
Here to elaborate on my comment. Is an example that will work. Try it with a simple tcp server. Start the client and then after a few seconds start the server. It is important to register you listeners after a reconnect happens in onError You may also want to have a limit of how many times you want to try to reconnect.
const net = require('net')
let client = connect()
client.on('data', onData);
client.on('error', onError);
client.on("close", onClose);
function onData(data) {
console.log(data)
}
function onError(err) {
if(err.message.indexOf('ECONNREFUSED') > -1) {
//do recconect
console.log("Attempting to reconnect shortly")
setTimeout(()=>{
client = connect();
client.on('data', onData);
client.on('error', onError);
client.on("close", onClose);
},1000)
}
}
function onClose() {
console.log("Removng all listeners")
client.removeAllListeners("data");
client.removeAllListeners("error")
}
function connect() {
const c = net.createConnection({
port: 3000
},
()=>{
console.log('connected')
});
return c
}

client is disconnecting from mosquitto broker and not connecting again to same broker

I am running mosquitto broker on 'iot.eclipse.org'.After publishing some commands to broker from my app server,client is disonnecting and calling reconnect and close methods continuously,but not connecting again unless I restart my server.But I want it to reconnect again,once connection breaks.
My code is as follows:
var options = {
host: 'iot.eclipse.org',
port: 1883,
};
var client = mqtt.connect(options);
client.on('connect', function (e) {
console.log("client is connected");
client.publish(topic, message,callback);
});
client.on("reconnect", function() {
console.log("client is reconnected",JSON.stringify());
})
client.on("error", function(err) {
console.log("error from client --> ", err);
})
client.on("close", function(e) {
console.log("client is closed",JSON.stringify(options),JSON.stringify(e));
})
client.on("offline", function(err) {
console.log("client is offline");
});
client.on('message', function (topic, message) {
console.log('*********');
console.log(message.toString());
})

Reconnect net.socket nodejs

I am new to node.js and would like to connect to a TCP socket. For this I am using the net module.
My idea was to wrap the connect sequence into a function then on the 'close' event, attempt a reconnection. Not that easy apparently.
function conn() {
client.connect(HOST_PORT, HOST_IP, function() {
startSequence();
})
}
client.on('close', function(e) {
log('info','Connection closed! -> ' + e)
client.destroy();
setTimeout(conn(),1000);
});
So when the remote host is closed, I see my logs comming through, howere what seems to be happening is that as soons as the remote host comes online ALL the previous attempts start to get processed - if that makes sense. If you look at client.connect, there is a function called startSequence that sends some data that "iniates" the connection from the remote server side. When the server goes offline and I start reconnecting all the failed attempts from before seem to have been buffered and are all sent together when the server goes online.
I have tried the code from this Stackoverflow link as well to no avail (Nodejs - getting client socket to try again after 5 sec time out)
client.connect(HOST_PORT, HOST_IP, function() {
pmsStartSequence();
})
// Add a 'close' event handler for the client socket
client.on('close', function(e) {
log('debug','connection closed -> ' + e)
client.setTimeout(10000, function() {
log('debug', 'trying to reconnect')
client.connect(HOST_PORT, HOST_IP, function() {
pmsStartSequence();
})
})
});
Is there any advice on how I can reconnect a socket after failure?
Inspired from the other solutions, I wrote this, it's tested, it works !
It will keep on trying every 5 sec, until connection is made, works if it looses connection too.
/* Client connection */
/* --------------------------------------------------------------------------------- */
const client = new net.Socket()
var intervalConnect = false;
function connect() {
client.connect({
port: 1338,
host: '127.0.0.1'
})
}
function launchIntervalConnect() {
if(false != intervalConnect) return
intervalConnect = setInterval(connect, 5000)
}
function clearIntervalConnect() {
if(false == intervalConnect) return
clearInterval(intervalConnect)
intervalConnect = false
}
client.on('connect', () => {
clearIntervalConnect()
logger('connected to server', 'TCP')
client.write('CLIENT connected');
})
client.on('error', (err) => {
logger(err.code, 'TCP ERROR')
launchIntervalConnect()
})
client.on('close', launchIntervalConnect)
client.on('end', launchIntervalConnect)
connect()
The problem is where you set the on-connect callback.
The doc of socket.connect() says:
connectListener ... will be added as a listener for the 'connect' event once.
By setting it in socket.connect() calls, every time you try reconnecting, one more listener (a one-time one), which calls startSequence(), is attached to that socket. Those listeners will not be fired until reconnection successes, so you got all of them triggered at the same time on a single connect.
One possible solution is separating the connect listener from socket.connect() calls.
client.on('connect', function() {
pmsStartSequence();
});
client.on('close', function(e) {
client.setTimeout(10000, function() {
client.connect(HOST_PORT, HOST_IP);
})
});
client.connect(HOST_PORT, HOST_IP);
My solution:
var parentHOST = '192.168.2.66';
var parentPORT = 9735;
var net = require('net');
var S = require('string');
var parentClient = new net.Socket();
var parentActive = false;
var startParentClient = function () {
parentClient = new net.Socket();
parentActive = false;
parentClient.connect(parentPORT, parentHOST, function() {
console.log('Connected ' + cluster.worker.id + ' to parent server: ' + parentHOST + ':' + parentPORT);
parentActive = true;
});
parentClient.on('error', function() {
parentActive = false;
console.log('Parent connection error');
});
parentClient.on('close', function() {
parentActive = false;
console.log('parent connection closed');
setTimeout(startParentClient(), 4000);
});
}
If is necessary connect:
if (!S(parentHOST).isEmpty() && !S(parentPORT).isEmpty()) {
startParentClient();
}
As mentioned multiple times in the comments, you need to use .removeAllListeners() before trying to reconnect your client to the server in order to avoid having multiple listeners on the same event.
The code below should do the trick
Note that I try to reconnect the client after the close and end events because these two events can be fired in different orders after closing a connection
const net = require("net")
let client = new net.Socket()
function connect() {
console.log("new client")
client.connect(
1337,
"127.0.0.1",
() => {
console.log("Connected")
client.write("Hello, server! Love, Client.")
}
)
client.on("data", data => {
console.log("Received: " + data)
})
client.on("close", () => {
console.log("Connection closed")
reconnect()
})
client.on("end", () => {
console.log("Connection ended")
reconnect()
})
client.on("error", console.error)
}
// function that reconnect the client to the server
reconnect = () => {
setTimeout(() => {
client.removeAllListeners() // the important line that enables you to reopen a connection
connect()
}, 1000)
}
connect()
I use the following code to achieve reconnection with node.js. I am not a Javascript expert so I guess it can be improved but it nevertheless works fine for me.
I hope this could help.
Best.
//----------------------------------------------------------------//
// SocketClient //
//----------------------------------------------------------------//
var net = require('net');
var SocketClient = function(host, port, data_handler, attempt)
{
var node_client;
var attempt_index = (attempt ? attempt : 1);
this.m_node_client = new net.Socket();
node_client = this.m_node_client;
this.m_node_client.on('close', function()
{
var new_wrapper = new SocketClient(host, port, data_handler, attempt_index + 1);
node_client.destroy();
new_wrapper.start();
});
this.m_node_client.on('data', data_handler);
this.m_node_client.on('error', function(data)
{
console.log("Error");
});
this.start = function()
{
this.m_node_client.connect(port, host, function()
{
console.log('Connected ' + attempt_index);
});
};
};
//----------------------------------------------------------------//
// Test //
//----------------------------------------------------------------//
var test_handler = function(data)
{
console.log('TestHandler[' + data + ']');
};
var wrapper = new SocketClient('127.0.0.1', 4000, test_handler);
wrapper.start();
I have tried re-using the same socket connection, by using this:
const s = net.createConnection({port});
s.once('end', () => {
s.connect({port}, () => {
});
});
that didn't work, from the server-side's perspective. If the client connection closes, it seems like a best practice to create a new connection:
const s = net.createConnection({port});
s.once('end', () => {
// create a new connection here
s = net.createConnection(...);
});
sad but true lulz.
Following this:
//
// Simple example of using net.Socket but here we capture the
// right events and attempt to re-establish the connection when
// is is closed either because of an error establishing a
// connection or when the server closes the connection.
//
// Requires
const net = require('net');
// Create socket
const port = 5555;
const host = '127.0.0.1';
const timeout = 1000;
let retrying = false;
// Functions to handle socket events
function makeConnection () {
socket.connect(port, host);
}
function connectEventHandler() {
console.log('connected');
retrying = false;
}
function dataEventHandler() {
console.log('data');
}
function endEventHandler() {
// console.log('end');
}
function timeoutEventHandler() {
// console.log('timeout');
}
function drainEventHandler() {
// console.log('drain');
}
function errorEventHandler() {
// console.log('error');
}
function closeEventHandler () {
// console.log('close');
if (!retrying) {
retrying = true;
console.log('Reconnecting...');
}
setTimeout(makeConnection, timeout);
}
// Create socket and bind callbacks
let socket = new net.Socket();
socket.on('connect', connectEventHandler);
socket.on('data', dataEventHandler);
socket.on('end', endEventHandler);
socket.on('timeout', timeoutEventHandler);
socket.on('drain', drainEventHandler);
socket.on('error', errorEventHandler);
socket.on('close', closeEventHandler);
// Connect
console.log('Connecting to ' + host + ':' + port + '...');
makeConnection();
function createServer() {
const client = new net.Socket();
client.connect(HOST_PORT, HOST_IP, function() {
console.log("Connected");
state = 1 - state;
client.write(state.toString());
});
client.on("data", function(data) {
console.log("Received: " + data);
//client.destroy(); // kill client after server's response
});
client.on("close", function() {
console.log("Connection closed");
//client.connect()
setTimeout(createServer, 2000);
});
}
createServer();

Resources