Overview
RabbitMQ is a message broker implementing AMQP. Producers publish to exchanges, and exchanges route messages to queues using bindings: direct (exact routing key), topic (wildcard patterns), fanout (all bound queues), or headers. Consumers receive messages from queues and acknowledge them individually.
RabbitMQ shines at flexible routing, per-message acknowledgments, priorities, delayed and dead-lettered messages, and request-reply patterns. It is a traditional 'smart broker, simple consumer' design, whereas Kafka is 'simple broker, smart consumer' with retained logs.
Letters (messages) arrive at the sorting desk (exchange), which reads the address (routing key) and places each into the right mailboxes (queues) according to rules. Recipients pick up mail from their own boxes.
When to use it
- Task queues with complex routing rules.
- Per-message acks, retries, priorities, and TTLs.
- Request-reply (RPC over messaging).
- Moderate throughput with low latency.
Where it shows up in interviews
Recognize it when: different message types to different workers.
- Design a notification router (SMS, email, push)
- Design a job system with priorities
Recognize it when: request-reply between services asynchronously.
- Design a background PDF render service
Where it is used in real software
Python's popular task queue commonly uses RabbitMQ as its broker.
RabbitMQ's Raft-based replicated queues for high availability and data safety.
Managed RabbitMQ offerings.
Key terms
- Exchange
- Receives messages and routes them to queues.
- Binding / routing key
- Rule linking exchange to queue / label used for routing.
- Direct / topic / fanout exchange
- Exact match / wildcard pattern / broadcast.
- Prefetch (QoS)
- Max unacknowledged messages per consumer.
- Dead-letter exchange
- Where rejected or expired messages go.
How it works, step by step
- 1Declare exchanges and queues
Durable, with bindings and DLX settings.
- 2Publish with a routing key
For example notify.email.welcome.
- 3Exchange routes
To every queue whose binding matches.
- 4Consumers receive with prefetch
Limits in-flight messages per worker.
- 5Ack, nack, or reject
Ack removes; nack requeues or dead-letters.
Exchange types
Routing key: notify.email.welcome
| Exchange type | Binding | Delivered? |
|---|---|---|
| Direct | notify.email.welcome | Yes (exact match) |
| Topic | notify.email.* | Yes (* matches one word) |
| Topic | notify.# | Yes (# matches zero or more words) |
| Topic | notify.sms.* | No |
| Fanout | (ignored) | Yes, to every bound queue |
NOWExchange type: Direct | Binding: notify.email.welcome | Delivered?: Yes (exact match)
Topic exchanges give flexible routing without producers knowing which queues exist.
Implementation
import amqp from "amqplib"; const conn = await amqp.connect(process.env.AMQP_URL!);const ch = await conn.createChannel(); await ch.assertExchange("notify", "topic", { durable: true });await ch.assertExchange("notify.dlx", "fanout", { durable: true });await ch.assertQueue("email-jobs", { durable: true, arguments: { "x-queue-type": "quorum", "x-dead-letter-exchange": "notify.dlx" } });await ch.bindQueue("email-jobs", "notify", "notify.email.*"); // Publish (persistent)ch.publish("notify", "notify.email.welcome", Buffer.from(JSON.stringify({ userId: 42 })), { persistent: true }); // Consume with prefetch and explicit acksawait ch.prefetch(20);await ch.consume("email-jobs", async (msg) => { if (!msg) return; try { await sendEmail(JSON.parse(msg.content.toString())); ch.ack(msg); } catch { ch.nack(msg, false, false); // do not requeue: route to dead-letter exchange }});Complexity and performance
Per cluster, depends on persistence.
Low for small messages.
Trade-offs
RabbitMQ routes flexibly with per-message acks; Kafka offers higher throughput and replay but simpler routing.
Very long queues consume memory and slow the broker; RabbitMQ prefers queues that are consumed promptly.
Variants and related techniques
RabbitMQ Streams add Kafka-like append-only logs with replay.
Priority queues and a delayed-message plugin.
Common mistakes
- Auto-ack mode.
Fix: Messages are lost if the consumer crashes; use manual acks.
- Unlimited prefetch.
Fix: One consumer grabs everything; set prefetch.
- Requeueing poison messages forever.
Fix: Dead-letter after a retry limit.
Interview questions
When would you choose RabbitMQ over Kafka?
For task queues needing flexible routing, per-message acknowledgments, priorities, delays, or request-reply, at moderate throughput. Choose Kafka for high-volume event streams with multiple consumers and replay.
What does prefetch do?
It limits the number of unacknowledged messages delivered to a consumer, so work is distributed evenly and a slow consumer does not hoard messages.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design routing for email, SMS, and push notifications | Easy | Topic exchanges. |
| Add retries with delays and a DLQ | Medium | DLX and TTL. |