Does node js worker thread and rabbitmq consumer are same? - node.js

This is to gain more knowledge on how rabbitmq queuing and node js master/worker threads combined.

Node.js master worker threads are different then rabbitmq queuing as rabbitmq provides the facility to store the tasks into queue so that they can be consumed by a worker process when a worker is free. Combining these two would have very specific use cases and generally not needed.
There are couple of things required for the combined implementation of these two which mainly includes node-amqp client and cluster. Cluster is the default feature of node which provides the api for master worker threads. Without rabbitmq you would generally distribute the tasks using one master process i.e. send the task to all worker process and worker threads listens to receive the tasks.
Now since you want to use rabbitmq you have to first subscribe to a exchange to listen for all the tasks and when you receive the task you pass that to your worker process. Below is an small snippet to provide the gist of explaination.
connection.on('ready', function() {
connection.exchange('exchange-name', function(exchange) {
_exchange = exchange;
connection.queue('queue-name', function(queue) {
_queue = queue;
// Bind to the exchange
queue.bind('exchange-name', 'routing-key');
// Subscribe to the queue
queue
.subscribe(function(message) {
// When receiving the message call the worker thread to complete the task
console.log('Got message', message);
queue.shift(false, false);
})
.addCallback(function(res) {
// Hold on to the consumer tag so we can unsubscribe later
_consumerTag = res.consumerTag;
})
;
});
});
});
Message exchange between master and worker: Instead of directly sending message to master worker needs to put the success message to a queue. The master will listen to that queue to receive the acknowledgements and success messages.

Related

Unsubscribe from a specific queue using node-amqplib on reconnect

problem: remote systems reconnect to multiple nodes websocket server, for each system a dedicated queue in RabbitMQ is created/used. The queues should be automatically removed if no active connections exist. Websocket connect/disconnect events handlers are asynchronous, quite heavy, observed problem that a disconnect event handler finished after reconnect, making system inconsistent.
The main issue is with RabbitMQ queues - initial solution was to create unique queues for each connection and remove them on disconnect. Appeared to be heavy.
Second approach was to keep a dedicated queue per remote system (same queue name for any connection), the problem was that assertQueue added consumers for the same queue. Need to find way to remove stale queue consumers without removing the queue itself.
Solution is to store list of consumers per remote system and on disconnect event trigger cancel function with the olderst consumerTag, then update the list of queue consumers for the given remote system.
on remote system connect event
import { Replies } from "amqplib";
// bind callback function for queue-specific messages and store returned consumer description
const result: Replies.Consume = await channel.consume(queueName, this.onSomeMessage.bind(this));
// update consumers list for the connected remote system
const consumers: Array<string> | undefined = this.consumers.get(remoteId);
if (consumers === undefined) {
const consumersList: Array<string> = new Array();
consumersList.push(result.consumerTag);
this.consumers.set(remoteId, consumersList);
} else {
consumers.push(result.consumerTag);
}
on remote system disconnect event
// remove the oldest consumer in the list and update the list itself
// use cancel method of the amqp channel
const consumers = this.consumers.get(remoteId);
if (consumers === undefined) {
// shouldn't happen
console.error(`consumers list for ${remoteId} is empty`);
} else {
const consumerTag = consumers[0];
await this.rxchannel.addSetup(async (channel: ConfirmChannel) => {
await channel.cancel(consumerTag);
consumers.shift();
});
}
The code snippets are from some class' methods implementation (if you're wondering about "this").
Copyright notice (especially for German colleagues): the code from this answer can be used under Beerware (https://en.wikipedia.org/wiki/Beerware) or MIT license (whatever one prefers).

handling cluster modules in nodejs

I'm trying to learn cluster module and I come across this piece of code I just cant get my mind around it. First it fork childs with child_process module and there it use cluster.fork().process , I've used both cluster module and child_process in an express web-server separately i know cluster module works a load balancer.
But I cant get the idea of using them together. and there also something else, cluster is listening to those worker process and when ever a disconnect and possibly exit event is emitted to master it reforked a process , but here is the question lets assume email worker crashes and the master is going to fork it again how does it know it should fork email ? I mean shouldn't it pass an id which I cant see in this code.
var cluster = require("cluster");
const numCPUs = require("os").cpus().length;
if (cluster.isMaster) {
// fork child process for notif/sms/email worker
global.smsWorker = require("child_process").fork("./smsWorker");
global.emailWorker = require("child_process").fork("./emailWorker");
global.notifiWorker = require("child_process").fork("./notifWorker");
// fork application workers
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork().process;
console.log("worker started. process id %s", worker.pid);
}
// if application worker gets disconnected, start new one.
cluster.on("disconnect", function(worker) {
console.error("Worker disconnect: " + worker.id);
var newWorker = cluster.fork().process;
console.log("Worker started. Process id %s", newWorker.pid);
});
} else {
callback(cluster);
}
but here is the question lets assume email worker crashes and the
master is going to fork it again how does it know it should fork email
? I mean shouldn't it pass an id which I cant see in this code.
The disconnect event it is listening to comes from the cluster-specific code, not a generic process listener. So, that disconnect event only fires when one of the cluster child processes exits. If you have some other child processes processing email, then when one of those crashes, it would not trigger this disconnect event. You would have to monitor that child_process yourself separately from within the code that started it.
You can see where the monitoring is for the cluster.on('disconnect', ...) event here in the cluster source code.
Also, I should mention that the cluster module is when you want pure horizontal scaling where all new processes are sharing the exact same work, each taking new incoming connections in turn. The cluster module is not for firing up a specific worker to carry out a specific task. For that, you would use either the Worker Threads module (to fire up a thread) or the child_process module (to fire up a new child process with a specific purpose)

Node.js Kue - Pause workers in app with multiple instances

When using kue in an app that has multiple instances (say multiple containers in docker) that all use the same redis database, if you pause a worker, do you need to pause that worker on all instances or is that handled at the redis level and hence handled for you?
https://github.com/Automattic/kue#pause-processing
queue.process('email', function(job, ctx, done){
ctx.pause( 5000, function(err){
console.log("Worker is paused... ");
setTimeout( function(){ ctx.resume(); }, 10000 );
});
});
It'd be great if we didn't have to use any instance-to-instance communication to get all workers to pause.
From reading through the code, it appears that pausing a worker simply cleans up the redis client for that worker and stops listening for events from redis.
Here is the relevant code.
https://github.com/Automattic/kue/blob/master/lib/queue/worker.js#L288-L302
So there is nothing that would prevent another instance of the same application from continuing to process messages. Therefore, if you have multiple instances of the same application running, and you want to pause a worker, you must signal to every instance of the application that a pause event has occurred.
We're using the pubsub functionality in redis to signal to all workers that they should pause/resume.

RabbitMQ - How to Detect consumer queue has been destroyed?

all
I am developing application in which client is creating queue with rabbitmq server, and there is also another server that will manage all the calculation over server.
I am facing problem with detection of queue destroy at server side.
For example : queue with name "a" created from android client, and now user is going to kill the app, that time queue will destroy. but server still in unknown state of that queue. so i am want to listen any event or any method to call for detect queue has been destroy from client side.
Thanks
You should use the Consumer Cancel Notification.
If you're using the node-amqp module, you should set the consumer_cancel_notify to true and, while subscribing to a queue, check for basicCancel.
q.subscribe(function (message, headers, deliveryInfo, messageObject) {
// Check for basicCancel
});

Parallel consumption of messages on different queues +rabbitmq+nodejs

It looks like the only library that's there for writing nodejs apps over rabbitmq is the
https://github.com/postwait/node-amqp
I have a producer which is posting messages to multiple queues at a very fast rate, and in the consumer I'm creating subscriptions for each queue.
connection.on('ready', function () {
for(var i=0;i<queues.length;i++)
connection.queue(queues[i],{autoDelete:false}, qOnReady);
});
function qOnReady(q){
// Catch all messages
logger("Q "+q.name+" is ready");
q.bind('#');
// Receive messages
q.subscribe(subscriber);
}
But when I run the consumer all the messages that are consumed are from a particular queue and till the queue is exhausted , the subscription doesn't start on the other queue.How do I achieve consume messages in parallel.
You might want to use Async Library to achieve parallel execution of your queues.
Though not sure if it fits your needs.
https://github.com/caolan/async#paralleltasks-callback

Resources