0

I am using Rascal.Js(it uses amqplib) for my messaging logic with rabbitMq on node.js app.

I am using something similar to their example on my project startup, which creates a permanent instance and "registers" all of my subscribers and redirects messages when they arrive to the queue (in the background).

My issue is with the publishers. There are http requests from outside which should trigger my publishers. A user clicks on create button of sorts which leads to certain flow of actions. At some point it reaches the point at which I need to use a publisher.

And here I am not sure about the right approach. Do I need to open a new connection every time I need to publish a message? and close it after it ends? Or maybe I should implement this in a way that it keeps the same connection open for all of the publishers? (I actually not so sure how to create it in a way that it can be accessed from other parts of my app).

At the moment I am using the following :

async publishMessage(publisherName, message) {
        const dynamicSettings = setupDynamicVariablesFromConfigFiles(minimalPublishSettings);
        const broker = await Rascal.BrokerAsPromised.create(Rascal.withDefaultConfig(dynamicSettings.rascal));

        broker.on('error', async function(err) {
            loggerUtil.writeToLog('error', 'publishMessage() broker_error_event: ' + publisherName + err + err.stack);
            await broker.shutdown();
        })
   
        const publication = await broker.publish(publisherName, message);
        try {
            publication.on('error', async function(err) {
                loggerUtil.writeToLog('error', 'publishMessage() publish_error_event: ' + err + err.stack);
                await broker.shutdown();
            }).on("success", async (messageId) => {
                await broker.shutdown();
            }).on("return", async (message) => {
                loggerUtil.writeToLog('error', 'publishMessage() publish_return_event: ' + err + err.stack);
                await broker.shutdown();
            })
        }
        catch(err) {
            loggerUtil.writeToLog('error', 'Something went wrong ' + err + err.stack);
            await broker.shutdown();
        }

    }

I use this function from different parts of my app when I need to publish messages. I thought to just add the broker.shutdown() for all of the endpoints but at some point after an error, I got an exception about closing a connection which was already closed, and this got me worried about the shutdown approach (which probably not a good one). I think it is related to this - I tried doing that (the commented code) but I think it isnt working well in certain situations. If everything is ok it goes to "success" and then I can close it. But one time I had an error instead of success and when I tried to use broker.shutdown() it gave me another exception which crashed the app. I think it is related to this - https://github.com/squaremo/amqp.node/issues/111

I am not sure what might be the safest way to approach this?

Edit:

Actually now that I think about it, the exception might be related to me trying to shutdown the broker in the catch{} area as well. I will continue to investigate.

Ivgi
  • 541
  • 1
  • 6
  • 21
  • I think it is best for every request you open the connection and after the message sends to the queue, you have to close the connection. because you cannot share states between requests by the security concerns and etc... – Babak Abadkheir Sep 28 '20 at 08:13
  • I tried doing that (the commented code) but I think it isnt working well in certain situations. If everything is ok it goes to "success" and then I can close it. But one time I had an error and when I tried to use broker.shutdown() it gave me another exception which crashed the app. I think it is related to this - https://github.com/squaremo/amqp.node/issues/111 – Ivgi Sep 28 '20 at 08:30
  • The suggestion to open and close a connection because you cannot share states between requests is bad advice in this instance. The amqp protocol, amqp and rascal have been designed to support sharing a connection and even channels without security concerns (assuming you are publishing to a common vhost) – cressie176 Nov 27 '20 at 06:59

1 Answers1

2

Rascal is designed to be initiated once at application startup, rather than created per HTTP request. Your application will be extremely slow if you use it in this way, and depending on how many concurrent requests you need to handle, could easily exceed max number of connections you can make to the broker. Furthermore you will get none of the benefits that Rascal provides, such as failed connection recovery.

If you can pre-determine the queue or exchange you need to publish to, then configure Rascal at application start-up (prior to your http server), and share the publisher between requests. If you are unable to determine the queue or exchange until your receive the http request, then Rascal is not an appropriate choice. Instead you're better off using amqplib directly, but should still establish a shared connection and channel. You will have to handle connection and channel errors manually though, otherwise they will crash your application.

cressie176
  • 959
  • 7
  • 8
  • Can you please provide a simple example on how I can try and share the connection? Basically on my http request if everything goes well I need to publish 2 messages. one for specific mail queue and 1 for specific user queue (those queues wont change for this http request). The problem was that I didnt find how to access the rascal.js publish logic from my mail.js and user.js files without needing to create a separate function in the rascal.js file so they can publish by using it. So I guess my issue is basically, how to access rascal.js publishing from different files in my app? – Ivgi Nov 29 '20 at 12:54
  • 1
    Instantiate the broker, then pass it as an argument to the other files. I've created an example gist [here](https://gist.github.com/cressie176/6050a726ec809935b9c5af0ff1d940c1) – cressie176 Nov 29 '20 at 16:14