Source: Dev.to | Jan 29, 2026 (UTC)
Why do decoupled and scalable systems need a message broker?
This question guides this entire article, where we explore RabbitMQ, one of the most popular and mature message brokers in the open-source ecosystem. We will explore the fundamental concepts, the benefits that RabbitMQ offers, and in practice, show, through examples and code, how to send and consume messages.
Monolithic systems require all modules to “talk” directly to each other, creating strong dependencies and fragility. However, as applications grow and become increasingly distributed, the need to decouple components and services increases. This is why systems based on microservices architecture are growing.
In this type of architecture, a message broker acts as an intermediary between systems or microservices (producers and consumers): it receives messages from those who publish events and messages, and guarantees their delivery to those who consume and process them, in an asynchronous manner, allowing different parts of an application or separate systems to exchange information efficiently, even when they are not available at the same time.
In addition, it helps solve scalability and reliability problems and allows the integration of services developed in different languages.
The RabbitMQ architecture is simple: producers send messages to components called exchanges, which act as routers. The exchanges are configured with specific rules that determine which queues the messages should be sent to. Each queue, in turn, is consumed by one or more consumers, who read and process the messages according to their business logic.
RabbitMQ implements the AMQP (Advanced Message Queuing Protocol) protocol, is written in Erlang and adopts a multithreaded architecture, which allows high concurrency.
The management interface makes the tool attractive for simple maintenance and monitoring of queues, messages and connections.
One of the main practical applications of RabbitMQ is in e-commerce order processing. It is also widely used in system integration in various sectors, such as logistics, transport, and billing. It allows heterogeneous systems to share data reliably and in an organized manner, even in highly complex scenarios.
Imagine an online shopping scenario. When an order is placed, a message is sent to a distribution queue, where the service responsible for stock consumes it and confirms the product’s availability. Another message can be sent to the transport queue, where the logistics system organizes delivery. Simultaneously, a message is processed in the billing queue, generating the invoice. This efficient and asynchronous organization avoids delays and promotes greater flexibility in communication between systems. The separation into queues ensures that each service works independently and scalably.
RabbitMQ uses an asynchronous pattern, allowing messages to be processed independently of the consumer’s execution time. This means that producers can publish messages without having to wait for consumers to process them immediately. This approach decouples systems, ensuring that the data flow continues to flow even when consumers are temporarily unavailable or dealing with high workloads.
This operating model is ideal for distributed systems, allowing scalability, flexibility, and resilience in message processing. With the ability to organize and prioritize message traffic, RabbitMQ becomes an assertive solution for implementing asynchronous architectures in various types of applications.
-
Decoupling between components and systems: the producer does not directly know the consumer. This facilitates the maintenance and evolution of the system, as new consumers can be added without changing who produces the messages.
-
Resilience and fault tolerance: messages can be stored persistently. If a consumer fails, the messages remain in the queue until it or another consumer processes them.
-
Horizontal scalability: it is possible to add new workers to process messages as demand increases.
-
Integration of heterogeneous systems: RabbitMQ supports several languages (Python, Java, Ruby, Go, etc.), making it ideal for microservices architectures.
-
Reliability and performance: delivery confirmation, acknowledgment control, durable queues, and high throughput contribute to making systems robust.
-
Flexibility: different types of exchanges (direct, topic, fanout, headers) allow for varied routing patterns.
-
Monitoring and management: the web administration interface provides graphs and statistics of messages, connections, channels, exchanges, and queues, facilitating operations.
This section covers the fundamental concepts of RabbitMQ and messaging in general.
RabbitMQ is a message-queueing software, meaning a message is the information to be queued and sent to the consumer service. A message can include any type of information. For example, it can contain information about a process or task that must be started in another application (which may even be on another server), a JSON with some system entity data, or it can just be a simple text message.
In the RabbitMQ context, a Producer is the system or application responsible for creating and sending messages to the broker in an exchange. It acts as the starting point in the messaging flow, generating information that needs to be processed or shared by other systems or services.
Producers do not interact directly with the queues where messages are stored. Instead, they send messages to the exchanges, which have the function of routing them to the appropriate queues based on the configured routing rules.
In an e-commerce application, for example, the system responsible for registering new orders acts as a producer, sending messages to an exchange. These messages may contain order information, such as the ID, purchased items, and customer data. The exchange then routes these messages to specific queues, where they will be consumed by services, such as stock control, billing, and logistics, for example.
The producer’s role is fundamental, as it initiates the communication process, ensuring that messages are delivered to RabbitMQ for later processing by consumers. This structure allows producers to focus only on creating and sending messages, while RabbitMQ manages the entire messaging flow.
Consumers are the systems or applications responsible for consuming messages from queues. They act as the recipients of messages sent by producers and stored by the broker, processing the data according to the necessary business logic.
Consumers connect to specific queues and read the available messages asynchronously, or on demand, depending on the configuration. This flexibility allows the consumer to process messages at their own pace, ensuring that the system works efficiently even in high-load scenarios.
For example, in an e-commerce application, a consumer can be responsible for reading messages from an order queue and, based on this information, performing tasks such as updating inventory, generating an invoice, or triggering the shipping system.
Consumers transform the messages stored in the queues into concrete actions, allowing RabbitMQ to integrate various systems and ensure fluid communication between them.
It is possible to have multiple consumers competing for messages from the same queue.
A Queue is the location where messages are temporarily stored until they are consumed. It works as a buffer that ensures that the data sent by producers remains available to consumers, even if they are not ready to process it immediately.
Queues are organized sequentially, ensuring that messages are delivered in the order they were received. In addition, they can be configured with different properties, such as durability (Time-To-Live – TTL), to ensure that messages remain stored even in the event of a system restart, and auto-delete when there are no consumers.
Using e-commerce as an example again, messages related to new orders can be stored in a specific queue until they are consumed by stock control or payment processing services. This structure allows each consumer to process messages at their own pace, without directly depending on the producers’ execution time.
It is the entity responsible for routing messages to one or more queues. The type of exchange determines how the message will be routed.
The direction of messages by an Exchange is essential to organize the message flow in distributed systems, allowing different types of data or events to be directed to the appropriate services.
For example, in a logistics application, an exchange can receive messages from a producer that reports package shipping events and status updates. Based on the configured rules, the exchange can route them to different queues: one queue for the transport service and another for the tracking system.
There are several types of exchanges:
-
Direct exchange: Routing based on an exact routing key. Useful for delivering a message to a specific queue.
-
Topic exchange: Uses wildcards in the routing key to match patterns. The character
*represents one word and#represents zero or more words. -
Fanout exchange: Ignores the routing key and sends the message to all queues bound to the exchange. Excellent for disseminating real-time events.
-
Headers exchange: Routing based on attributes defined in the message headers, not on the routing key.
The routing key is a string that the producer sends along with the message. The exchange uses this key to decide which queue to deliver the message to. A binding connects a queue to an exchange and defines the routing rule, for example: the logs.error queue can be associated with a log.error routing key. In a topic exchange, you can use log.* to receive all logs of a certain type and log.# to receive all logs.
In other words:
-
Binding: Bindings are rules that exchanges use (among other things) to route messages to queues.
-
Routing key: A key that the exchange uses to decide how to route the message to the queues. Think of the routing key as an address for the message.
It is possible to connect to RabbitMQ with a specific username and password. Users can have associated permissions such as read, write, and configuration privileges within the instance (read, write, and configure). Users can also be given permissions for specific virtual hosts.
For production environments, delete the default user (guest)!.
The default user can only connect from localhost, as it has known credentials. Instead of enabling remote connections, consider creating a separate user with administrative permissions and a strong password. It is recommended to use a separate user per application. For example, if you have a mobile application, a web application, and a data aggregation system, create 3 separate users. This facilitates several things:
- Correlate client connections with applications.
- Use fine-grained permissions.
- Credential renewal – roll-over (for example, periodically or in case of a security breach).
RabbitMQ is a multi-tenant system: connections, exchanges, queues, bindings, user permissions, policies and other elements belong to virtual hosts (vhosts), logical groups of entities.
Virtual hosts provide logical grouping and separation of resources. Users can have different permissions for different vhosts, and queues and exchanges can be created to exist only in one vhost.
A virtual host has a name. When a client connects to RabbitMQ, it specifies a vhost name to connect to. If authentication is successful and the provided username has permissions for the vhost, the connection will be established.
In addition to the simple work queue pattern, where several instances process tasks concurrently, there are also other patterns:
-
Pub/Sub (publish/subscribe): with the fanout exchange, the same message is delivered to all consumers, useful for notification and broadcast events.
-
Work queues: when several instances consume from the same queue, improving task distribution.
-
RPC (request/reply): a client sends a message requesting a result and waits for the response in the queue indicated in the
reply_toheader. The correlation is done by acorrelation_id.
Installation and Configuration
The simplest way to run RabbitMQ locally is by using Docker. Follow these steps to provision a local RabbitMQ container (run in a terminal with Docker installed):
# 1. Download the official image with the management plugin $ docker pull rabbitmq:3.13-management # 2. Start the container with the default ports (5672 for AMQP and 15672 for the web interface) $ docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.13-management # 3. Check the logs (optional) $ docker logs -f rabbitmq
Enter fullscreen mode
Exit fullscreen mode








Залишити відповідь