Node.js and RabbitMQ - best way to init connection on server start - node.js

Trying to implement RabbitMQ on my already existing codebase written on koa.js, faced the problem that I actually don't know what is the best way to do that. Most tutorials I faced in the web left me with idea establishing connection to RabbitMQ server each time I want to send or receive the message. That makes sense when I am receiving message in worker, but how to establish connection on provider side?
I read that that is a bad thing to establish connection each time when I create channel or send message. So the idea is that I need to create connection when I am starting server, so atm I do it like this:
const server = app.listen(PORT, async () => {
await rabbit.createConnection(`amqp://localhost:5672`);
global.rabbit = rabbit;
console.log(
`\n Server listening on port: ${PORT} in ${process.env.NODE_ENV} mode \n`
);
});
Is this a good spot or not?
Thanks for your advices!
P.S. In my rabbit instance I save the connection

Start the RabbitMQ connection once and keep the connection alive. Only reconnect if the connection should die for some reason. Whether you do this in your index.js or when you start Koa depends on your app, but in general it doesn't really matter as long as you are able to connect and shutdown properly.
Making a new connection for each publish or consume is insane from a performance perspective.
To simplify reconnections, try a amqp connection manager. It handles reconnects transparently.

Related

Node Js Web socket client stops listening

I have built websocket client in node js. I need to restart it after every 8-10 days. It suddenly stops listening to the websocket server. I have used the following lib for connection.
"ws": "^6.2.1"
I do restart connection and print logs in onclose and onerror listeners.
this.connection.onerror = error => {
rawDataStreamLog.info("Websocket ::: onerror " + JSON.stringify(error))
this.restartConnection();
};
this.connection.onclose = () => {
rawDataStreamLog.info("Websocket ::: onclose")
this.restartConnection()
};
Any insight would be greatly appreciated. Thanks.
It's very likely your server initiated the disconnection. Or maybe something went wrong with the network connection.
It has been my experience that websocket client code for long-lived connections should detect and handle close events by reconnecting. If things still fail after several attempts to reconnect you can investigate what's wrong at the server or in the network.
Does your server send websocket ping (keepalive) messages? If not, it should.

Node.js connection with RabbitMQ

I am having a node.js application that uses amqlib to connect with RabbitMQ.
I am trying to reproduce a connectivity error with RabbitMQ and I get two different errors by repeating the same flow.
What I am doing is:
Start a Docker container with RabbitMQ management.
Start a node.js application (either docker or with npm) that connects on the RabbitMQ.
Go on RabbitMQ management and with rabbitmqctl execute the stop_app
This flow produces, each time one of the below two exceptions (not sure how it decides each one):
OperationalError: connect ECONNREFUSED 172.24.0.3:5672
Error: Heartbeat timeout
Why does this happen? Also, what is the best approach to handle them?
This is my connect function on the connector that does not seem to cover the heartbeat exception:
async connect(): Promise<Connection> {
const conn = await amqp.connect({
protocol: AMQP_PROTOCOL,
hostname: RABBITMQ_HOST,
port: Number(RABBITMQ_PORT),
username: RABBITMQ_USER,
password: RABBITMQ_PASS,
vhost: RABBITMQ_VHOST
});
conn.on('error', this.onError);
conn.on('close', this.onClose);
logger.debug('Connected to amqp');
this.conn = conn;
this.emit('connect', conn);
return conn;
}
ECONNREFUSED means that the application could not connect to RabbitMQ inside the docker container.
The heartbeat error means that a connection was successfully established, but the client has stopped receiving heartbeats from the broker, indicating that the connection has been lost.
There is also another type of notification you might receive. If you have started consuming messages from your application, when you stop the broker, amqplib will deliver a null message to the consumer. If you're not expecting this it can often cause an error in your application.
Handling these different scenarios can be difficult. The simplest way is to attach handlers to all connections and channel, then to gracefully stop your application, and allowing whatever is managing it to automatically restart using a suitable backoff algorithm.
If this isn't acceptable, then you need to reconnect and reconsume from the handlers. You may also want to internally queue messages that are published, until the connection has been reestablished. I wrote Rascal to do just this. There's also amqp-connection-manager.
Other things you may consider using to test...
docker kill (rudely kill the connection)
docker pause (will cause a heartbeat timeout)
queue deletion (I believe this triggers a null message)

How should a Node.js microservice survive a Rabbitmq restart?

I've been working on an example of using Rabbitmq for communication between Node.js microservices and I'm trying to understand the best way for these microservices to survive a restart of the Rabbitmq server.
Full example is available on Github: https://github.com/ashleydavis/rabbit-messaging-example
You can start the system up by changing to the broadcast sub-directory and using docker-compose up --build.
With that running I open another terminal and issue the following command to terminate the Rabbit server docker-compose kill rabbit.
This causes a Node.js unhandled exception to kill my sender and receiver microservices that were connected to the Rabbitmq server.
Now I'd like to be able to restart the Rabbitmq server (using docker-compose up rabbit) and have the original microservices come back online.
This is intended to run under Docker-Compose for development and Kubernetes for production. I could just set this up so that the microservices restart when they are terminated by the disconnection from Rabbitmq, but I'd prefer it if the microservices could stay online (they might be doing other work that shouldn't be interrupted) and then reconnect to Rabbitmq automatically when it becomes available again.
Anyone know how to achieve automatic reconnection to Rabbitmq using the ampq library?
Just picking the sender service as an example on how to deal with it.
The error that is causing node to exit is that here is no 'error' handler on the stream the writer users.
If you modify this part of the code.
https://github.com/ashleydavis/rabbit-messaging-example/blob/master/broadcast/sender/src/index.js#L13
Change the line in sender/src/index.js from
const messagingConnection = await retry(() => amqp.connect(messagingHost), 10, 5000);
to
const messagingConnection = await retry(() => amqp.connect(messagingHost), 10, 5000)
.then(x => {
return x.on('error', (err) => {
console.log('connect stream on error', err)
});
});
Just having the error handler means that the node process no longer exists with unhandled exception. This does not make the sender code correct, it now needs to be modified to know if it has a connection, only send data if it has a connection, retry to connect if it has no connection.
A similar fix for the receiver can be applied
This is a useful reference for when node requires setup to not exit.
https://medium.com/dailyjs/how-to-prevent-your-node-js-process-from-crashing-5d40247b8ab2

Node.JS net module handling unexpected connection loss

I can't figure out one problem I got.
I'm using the Net module on my Node.JS server which is used to listen to client connections.
The client do connect to the server correctly and the connection remains available to read/write data. So far, so good. But when the client unexpectedly disconnects (ed. when internet falls away at client side) I want to fire an event server side.
In socket.io it would be done with the 'disconnect' event, but this event doesn't seem to exist for the Net module. How is it possible to do?
I've searched on Google/StackOverflow and in the Net documentation (https://nodejs.org/api/net.html) but I couldn't find anything usefull. I'm sry if I did mis something.
Here is a code snippet I got:
var net = require('net');
var server = net.createServer(function(connection) {
console.log('client connected');
connection.wildcard = false;//Connection must be initialised with a configuration stored in the database
connection.bidirectional = true;//When piped this connection will be configured as bidirectional
connection.setKeepAlive(true, 500);
connection.setTimeout(3000);
connection.on('close', function (){
console.log('Socket is closed');
});
connection.on('error', function (err) {
console.log('An error happened in connection' + err.stack);
});
connection.on('end', function () {
console.log('Socket did disconnect');
});
connection.on('timeout', function () {
console.log('Socket did timeout');
connection.end();
});
connection.on('data', function (data) {
//Handling incoming data
});
});
serverUmrs.listen(40000, function () {
console.log('server is listening');
});
All the events(close, end, error, timeout) don't fire when I disconnect the client(by pulling out the UTP cable).
Thanks in advance!
EDIT:
I did add a timeout event in the code here above but the only thing that happens is that the socket does timeout after 3 seconds everytime the client does connect again. Isn't KeepAlive enough to make the socket not Idle? How is it possible to make the socket not idle without to much overhead. It may be possible that there are more than 10,000 connections at the same time which must remain alive as long as they are connected (ie respond to the keepalive message).
Update:
I think the KeepAlive is not related with the Idle state of socket, sort of.
Here is my test, I remove the following code in your example.
//connection.setKeepAlive(true, 500);
Then test this server with one client connect to it var nc localhost 40000. If there is no message sending to server after 3 seconds, the server logs as below
Socket did timeout
Socket did disconnect
Socket is closed
The timeout event is triggered without KeepAlive setting.
Do further investigation, refer to the Node.js code
function onread(nread, buffer) {
//...
self._unrefTimer();
We know timeout event is triggered by onread() operation of socket. Namely, if there is no read operation after 3 seconds, the timeout event will be emitted. To be more precisely, not only onread but also write successfully will call _unrefTimer().
In summary, when the write or read operation on the socket, it is NOT idle.
Actually, the close event is used to detect the client connection is alive or not, also mentioned in this SO question.
Emitted when the server closes. Note that if connections exist, this event is not emitted until all connections are ended.
However, in your case
disconnect the client(by pulling out the UTP cable).
The timeout event should be used to detective the connection inactivity. This is only to notify that the socket has been idle. The user must manually close the connection. Please refer to this question.
In TCP connection, end event fire when the client sends 'FIN' message to the server.
If the client side is not sending 'FIN' message that event is not firing.
For example, in your situation,
But when the client unexpectedly disconnects (ed. when internet falls away at client side) I want to fire an event server side.
There may not be a 'FIN' message because internet is gone.
So you should handle this situation in timeout without using keepAlive. If there is no data coming data, you should end or destroy the socket.
EDIT: I did add a timeout event in the code here above but the only
thing that happens is that the socket does timeout after 3 seconds
everytime the client does connect again. Isn't KeepAlive enough to
make the socket not Idle? How is it possible to make the socket not
idle without to much overhead. It may be possible that there are more
than 10,000 connections at the same time which must remain alive as
long as they are connected (ie respond to the keepalive message).
For your edit, your devices should send to the server some heartbeat message between a time period. So that, server understands that that device is alive and that timeout event will not fire because you get some data. If there is no heartbeat message such cases you cannot handle this problem.

nodejs, how to make socket connection close to backend server after some timeout time

I am implementing web server using express module, the web server communicates with the backend server to get the data and send to UI.
Now if due to some reason the backend server does not send any data, then TCP connection does not get closed. How I can implement connection to backend server close after some timeout period?
I am not using socket.io module.
Regards,
-M-
If you are using Socket you can close the server connection using:
socket.on('timeout', function () {
socket.close();
});
You can also:
socket.setTimeout()
socket.setKeepAlive()

Resources