Search code examples
node.jsdockerrabbitmqamqpnode-amqp

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;
  }

Solution

  • 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)