Building Microservices with Node.js and RabbitMQ

  ·   3 min read

In modern software architecture, particularly in a microservices environment, different services need to interact with one another in an efficient and reliable manner. In this article, we will explore how to implement a simple communication system between two Node.js applications using RabbitMQ as our message broker.

Introduction to RabbitMQ

RabbitMQ is a robust, open-source message broker that allows applications to communicate with each other by sending messages. It’s an implementation of the Advanced Message Queuing Protocol (AMQP) and supports multiple messaging protocols. Its features, such as message queueing, routing, reliability, and asynchronous processing, make it a popular choice in microservices architectures.

Setting Up RabbitMQ

  1. Installation: If you don’t have RabbitMQ installed, you can do so via Docker to keep it simple:

    docker run -d --hostname rabbitmq --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
    

    After running the command, you can access the RabbitMQ management interface through http://localhost:15672 with default credentials (guest / guest).

  2. Libraries: For RabbitMQ communication in Node.js, we will use the amqplib library. Install it in both applications using npm:

    npm install amqplib
    

Application 1: Message Producer

Let’s create a simple message producer that sends messages to a queue. Create a file called producer.js.

const amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', (error, connection) => {
    if (error) throw error;

    connection.createChannel((error, channel) => {
        if (error) throw error;

        const queue = 'task_queue';
        const msg = process.argv.slice(2).join(' ') || 'Hello World';

        channel.assertQueue(queue, {
            durable: true
        });

        channel.sendToQueue(queue, Buffer.from(msg), {
            persistent: true
        });

        console.log(`[x] Sent '${msg}'`);
    });

    setTimeout(() => {
        connection.close();
        process.exit(0);
    }, 500);
});

Application 2: Message Consumer

Next, we will create a message consumer that receives messages from the queue. Create a file called consumer.js.

const amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', (error, connection) => {
    if (error) throw error;

    connection.createChannel((error, channel) => {
        if (error) throw error;

        const queue = 'task_queue';

        channel.assertQueue(queue, {
            durable: true
        });

        channel.prefetch(1);
        console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queue);

        channel.consume(queue, (msg) => {
            const seconds = msg.content.toString().split('.').length - 1;

            console.log(" [x] Received '%s'", msg.content.toString());
            setTimeout(() => {
                console.log(" [x] Done");
                channel.ack(msg);
            }, seconds * 1000);
        }, {
            noAck: false
        });
    });
});

Running the Applications

  1. Start the Consumer: Before you send messages, you need to run the consumer first.

    node consumer.js
    
  2. Start the Producer: You can send messages using the producer. Open another terminal and run:

    node producer.js "Message 1"
    

    You can also send multiple messages.

  3. Monitor the output: In your consumer terminal, you should see the messages being received and processed.

Conclusion

RabbitMQ provides a significant advantage by decoupling applications and allowing them to communicate asynchronously. By employing a message broker such as RabbitMQ, we can improve the resilience and scalability of our Node.js applications.

Using Node.js and RabbitMQ together, you can build microservices that are responsive, fault-tolerant, and efficient in handling communication between various components.

References