RabbitMQ

Introduction

RabbitMQ is an open-source distributed message broker written in Erlang that facilitates efficient message delivery and complex routing scenarios. RabbitMQ typically runs as a cluster of nodes where queues are distributed across nodes.

RabbitMQ natively implements AMQP 0.9.1 and uses plug-ins to offer additional protocols like AMQP 1.0, HTTP, STOMP, and MQTT. RabbitMQ ecosystem has a lot of client libraries in different programming languages.

RabbitMQ is perfect for web servers that need rapid request-response. It also shares loads between workers under high load (20K+ messages/second). RabbitMQ can also handle background jobs or long-running tasks like PDF conversion, file scanning, or image scaling.

The current article explains the basic concepts and features of the RabbitMQ message broker. RabbitMQ is shipped with AMQP 0.9.1 protocol, so it’s recommended to look at the AMQP description first.

Queues

Queues are a buffer that stores messages. In RabbitMQ, queues follow FIFO (“First In, First Out”) manner. However, some RabbitMQ features could affect this behavior, and change the way of receiving queue messages by consumers.

Queue supports two primary operations:

  • enqueue - add an item to the tail of the queue
  • dequeue - extract an item from the head of the queue

Optional Arguments

Along with standard AMQP 0.9.1 queue arguments, RabbitMQ supports a bunch of optional ones (called x-arguments):

ArgumentDescription
x-queue-typeQuorum or Classic
x-message-ttlTTL (time to live) for queue messages in ms
x-max-lengthMaximum number of messages
x-max-priorityDeclare priority queue and set maximum priority that queue should support

Temporary Queues

For some tasks, queues could be short-living and must be deleted once the consumer is disconnected. There are a few options to make the queue deleted automatically:

  • Exclusive queues

    Can be only used by its declaring connection. They are automatically deleted when declaring connection is closed. It’s common to declare them without a name to make the server generate them.

  • TTL queues

    Used for data expiration and as a way of limiting the usage of system resources when consumers go offline or their throughput falls behind publishers.

  • Auto-delete queues

    Will be automatically deleted when the last consumer is disconnected. If a queue never had consumers it won’t be deleted.

Priority Queues

Queues can have 0 or up to 255 priorities. A queue becomes a priority queue when x-max-priority argument is passed at the queue declaration time.

Then publishers could specify a priority attribute to publishing messages.

Messages

Message states

The message in a queue can have one of two states:

  • Ready for delivery
  • Delivered but not yet acknowledged by consumer

Messages limit

There is a possibility to specify the length of a queue in bytes or messages count. That’s could be done using queue declaration arguments or via a queue policy.

See more details at Queue Length Limit.

Publishers

Publishers publish messages to different destinations depending on the used protocol:

  • AMQP 0-9-1 Publishers publish to exchanges
  • AMQP 1.0 Publishers publish on a link
  • MQTT Publishers publish to topic
  • STOMP Publishers supports various publish destinations: topics, queues, AMQP 0-9-1 exchanges

Normally, publishers are long-living and usually open their connection during application startup to publish multiple messages.

Message Properties

Every delivery has message metadata and delivery information.

Delivery information is set by RabbitMQ at routing and delivery time
PropertyTypeDescription
Delivery tagIntegerDelivery identifier
RedeliveredBooleanSet to true if this message was previously delivered and requeued
ExchangeStringExchange which routed this message
Routing keyStringRouting key used by the publisher
Consumer tagStringConsumer (subscription) identifier
Message metadata is set by publishers at the time of publishing
PropertyTypeDescription
Delivery modeEnum (1 or 2)2 for “persistent”, 1 for “transient”. Some client libraries expose this property as a boolean or enum.
TypeStringApplication-specific message type, e.g. “orders.created”
HeadersMap (string => any)An arbitrary map of headers with string header names
Content typeStringContent type, e.g. “application/json”. Used by applications, not RabbitMQ
Content encodingStringContent encoding, e.g. “gzip”. Used by applications, not RabbitMQ
Message IDStringUnique message ID
Correlation IDStringHelps correlate requests with responses
Reply ToStringCarries response queue name
ExpirationStringPer-message TTL is milliseconds
TimestampTimestampApplication-provided timestamp
User IDStringUser ID, validated if set
App IDStringApplication name

Consumer Acknowledgements and Publisher Confirms

Delivery processing acknowledgements from consumers to RabbitMQ are known as acknowledgements in messaging protocols.

Broker acknowledgements from RabbitMQ to publishers are a protocol extension called publisher confirms.

When a consumer is registered, messages will be delivered by RabbitMQ using the basic.delivery method. This method has a delivery tag that is identifies the delivery on a channel. Delivery tags are scoped per channel.

Delivery tags are sequentially incremented positive integers which is used by client library to acknowledge the delivery.

Manually sent acknowledgements can be positive or negative and use one of the following protocol methods:

  • basic.ack - positive acknowledgements which notifies RabbitMQ that a message was successfully processed and can be recorded as delivered and discarded
  • basic.reject - negative acknowledgements which notifies RabbitMQ that a message wasn’t successfully processed, however it’s delivered and should be deleted
  • basic.nack - negative acknowledgements (RabbitMQ extension to AMQP 0-9-1) that allows to batch acknowledgements for reducing the network traffic involved

In automatic acknowledgement mode, a message is considered to be successfully delivered immediately after it is sent. This mode is called “fire-and-forget” and provides higher throughput, but in the other hand, it’s unsafe because a message could be lost in case of TCP connection interruption.

Moving to production

Cluster Configuration

RabbitMQ cluster has a plenty of entities to be configured, such as virtual hosts, users, permissions, polices, topologies, queues, exchanges and binding. Instead of manual configuration of every mentioned thing, there is a possibility to seed all the required configurations thought the definitions file.

It might be helpful for creation new environments and raising the cluster configuration as a file using Infrastructure as code approach.

Definition files can be imported/exports via UI Management tool, RabbitMQ CLI or HTTP API. They are stored in a JSON format and could be used for the backups as well.

Dealing With Environment

In the case of running a few environments on the same RabbitMQ cluster, each environment should use a separate virtual host.

Virtual host provides a logical separation for the RabbitMQ resources, which means that all the users, permissions, queues, etc. belong to a specific virtual host and are not shared with each other.

Virtual hosts can be managed via RabbitMQ CLI, HTTP API or UI Management tool.

Users

  • Do not forget to remove the default guest user
  • Use a separate user per application with corresponding permissions

System Resources

By default, RabbitMQ will not accept any new messages if it’s using more than 40% of available memory. The rest of the memory is used by OS and file system to speed up cluster operations. This can be changed via vm_memory_high_watermark cluster configuration.

The default value of disk_free_limit is 50MB which is not sufficient for the production deployment and could lead to data loss.

There are a few recommendations:

  • Nodes hosting RabbitMQ cluster should have at least 256MiB of available memory
  • vm_memory_high_watermark.relative should have the value in range from 0.4 to 0.7
  • disk_free_limit.relative should have the value in range from 1.0 to 2.0

Logging and monitoring

The combination of Prometheus and Grafana is the highly recommended option for RabbitMQ monitoring.

All the logs from RabbitMQ cluster should be collected and aggregated if possible to investigate unusual system behavior.

Security

RabbitMQ nodes are securing communication between each other using shared secret cookie file, so it’s important to restrict this file access only to the OS user that running RabbitMQ and CLI tools.

There are two port categories used by RabbitMQ:

  • Ports that are used by clients (AMQP, MQTT, STOMP, HTTP API)
  • Other ports that are used by RabbitMQ for inner communication

That’s important to restrict accessing internal ports from the public networks.

It’s also recommended to use TLS connection to encrypt traffic.

Examples

Resources

Top