Learn the theory
RabbitMQ: Exchanges, Queues, Acks & Dead Letters: how it works
4 min read · Updated
RabbitMQ is a message broker: applications send messages to it, and other applications receive them, so the two sides don't have to be online, fast or written in the same language at the same time. Its distinctive idea is that producers never write directly to queues. They publish to an exchange, and bindings decide which queues receive a copy. Consumers read from queues and acknowledge each message; anything unacknowledged can be redelivered. Understanding those four words — exchange, binding, queue, ack — explains almost everything RabbitMQ does.
In short
- Producers publish to an exchange; bindings route messages to queues; consumers read from queues.
- Exchange types: direct (exact key), topic (wildcards
*and#), fanout (broadcast), headers. - A message is deleted only after the consumer acks it. If the consumer dies, it's redelivered — at-least-once delivery.
- Prefetch limits unacked messages per consumer. Too high and a slow consumer hoards work;
1gives fair dispatch. - Failed messages should be rejected without requeue into a dead-letter exchange/queue, not requeued forever.
The AMQP model
- A producer publishes a message with a routing key to an exchange.
- The exchange applies its routing rules using bindings (queue + binding key).
- Matching queues each get a copy and hold it until it's consumed.
- A consumer receives messages from a queue and sends an ack when it's done.
Separating the exchange from the queue is what makes RabbitMQ flexible: you can add a new consumer (a new queue + binding) without touching the producer.
Exchange types
| Type | Routing rule | Typical use |
|---|---|---|
| direct | Delivers to queues whose binding key equals the routing key. | Targeted routing: error → alert queue. |
| topic | Pattern match on dot-separated words: * = exactly one word, # = zero or more. | Event buses: order.created, *.created, order.#. |
| fanout | Ignores the key; copies to every bound queue. | Broadcast / publish-subscribe. |
| headers | Matches on message headers instead of the key. | Rare; complex attribute routing. |
| default (nameless) | Routes to the queue whose name equals the routing key. | Simple work queues. |
Competing consumers and prefetch
Several consumers on the same queue compete: each message goes to exactly one of them, which spreads work. Each consumer can hold up to prefetch unacknowledged messages.
prefetch = 1: a consumer only receives a new message after acking the last one — fair dispatch; a slow worker doesn't hoard.- Large prefetch: higher throughput for tiny messages, but a slow consumer can sit on many messages while a fast one idles (the lab's “prefetch = 10” preset).
Acknowledgements and redelivery
With manual acks, RabbitMQ keeps a delivered message in the queue marked unacked until the consumer confirms. If the consumer's connection closes without acking, the broker requeues the message (redelivered=true) for another consumer.
ack: done — delete the message.nack/rejectwithrequeue=true: put it back at the head of the queue.nack/rejectwithrequeue=false: discard it, or dead-letter it if a DLX is configured.
Dead-letter exchanges and poison messages
A message that always fails (a poison message) will loop forever if you keep requeueing it, burning CPU and blocking the head of the queue. Instead, configure a dead-letter exchange (DLX) on the queue. Messages that are rejected without requeue, expire (TTL) or overflow a max-length are routed to a dead-letter queue where you can inspect, alert and replay them.
await ch.assertQueue('tasks', {
arguments: {
'x-dead-letter-exchange': 'dlx',
'x-dead-letter-routing-key': 'tasks.dead',
},
});
// in the consumer on permanent failure:
ch.nack(msg, false, false); // requeue = false → goes to the DLQMaking it reliable
- Declare queues and exchanges durable and publish persistent messages to survive broker restarts.
- Use publisher confirms so the producer knows the broker accepted a message.
- Use quorum queues for replicated, highly available queues.
- Cap retries with a retry count header or delayed-retry queues, then dead-letter.
- Monitor queue length, unacked count and consumer utilization.
RabbitMQ vs BullMQ vs Kafka
| RabbitMQ | BullMQ | Kafka | |
|---|---|---|---|
| Model | Smart broker, routing, per-message acks | Redis-backed job queue | Distributed append-only log |
| Best for | Routing and messaging between services | Background jobs in Node.js | High-volume event streaming, replay |
| Message after consume | Deleted on ack | Kept briefly, then removed | Retained; consumers track offsets |
| Languages | Many (AMQP) | Node.js (and Python/Elixir clients) | Many |
Frequently asked questions
- What is an exchange in RabbitMQ?
- A routing component that receives messages from producers and distributes them to queues according to its type (direct, topic, fanout or headers) and the bindings between it and queues.
- What is the difference between direct, topic and fanout exchanges?
- Direct matches the routing key exactly, topic matches wildcard patterns (* for one word, # for zero or more), and fanout ignores the key and copies the message to all bound queues.
- What does prefetch do in RabbitMQ?
- It limits how many unacknowledged messages the broker will deliver to a consumer at once. A prefetch of 1 gives fair dispatch among competing consumers; a high value increases throughput but can starve other consumers.
- What happens if a RabbitMQ consumer crashes before acking?
- The broker detects the closed connection and requeues the unacked messages (marked redelivered) so another consumer can process them. This gives at-least-once delivery.
- What is a dead-letter queue?
- A queue that receives messages which were rejected without requeue, expired or exceeded a queue limit, via a dead-letter exchange. It lets you inspect and replay failed messages instead of losing them or looping.
- Does RabbitMQ guarantee message ordering?
- Messages are ordered within a single queue for a single consumer, but ordering is not guaranteed with multiple consumers, redeliveries or requeues.