RabbitMQ is a mature, widely-deployed message broker. It accepts messages from producers and routes them to consumer queues based on rules you configure, then removes each message once a consumer acknowledges it. Its strength is flexible routing and reliable work distribution - task queues, request/reply, and decoupling microservices - rather than the durable, replayable log that Kafka provides.
Exchanges, bindings, and queues
RabbitMQ's routing model is its defining feature. Producers publish to an exchange, not directly to a queue. Bindings connect exchanges to queues with rules, and the exchange type decides how messages flow:
- direct - route by exact routing-key match (e.g., send "orders.eu" to the EU queue).
- topic - route by wildcard patterns (
orders.*.priority). - fanout - broadcast to every bound queue, ignoring keys.
- headers - route on message header attributes.
This lets one publish reach many queues, or be filtered to one, entirely through configuration rather than producer code.
Acknowledgements and reliability
A consumer acknowledges (ack) a message after processing; until then RabbitMQ keeps it and will redeliver if the consumer dies. This gives at-least-once delivery, so handlers must be idempotent. Failed messages can be routed to a dead-letter exchange for inspection or retry. Durable queues plus persistent messages survive a broker restart, and publisher confirms tell the producer the broker has taken responsibility for a message.
import pika
conn = pika.BlockingConnection(pika.ConnectionParameters("broker"))
ch = conn.channel()
ch.queue_declare(queue="tasks", durable=True)
ch.basic_publish(exchange="", routing_key="tasks",
body="job-42",
properties=pika.BasicProperties(delivery_mode=2)) # persistent
# consumer
ch.basic_qos(prefetch_count=1) # fair dispatch: one unacked at a time
for method, props, body in ch.consume("tasks"):
handle(body)
ch.basic_ack(method.delivery_tag)
Work queues and prefetch
The classic use is a work queue: many worker consumers pull from one queue and RabbitMQ load-balances tasks across them. Setting prefetch_count=1 gives fair dispatch - a worker won't be handed a new task until it acks the current one, so a slow job doesn't pile up behind one busy worker while others idle.
RabbitMQ vs Kafka
This is the decision most teams actually face. RabbitMQ removes a message once consumed and routes per-message - ideal for task distribution, RPC, and complex routing. Kafka retains an ordered log that many consumers replay independently - ideal for event streaming, multiple downstream consumers of the same data, and reprocessing history. Choose RabbitMQ when you want a smart broker doing flexible routing and queue semantics; choose Kafka when you want a durable, replayable event log and high sustained throughput. Newer RabbitMQ streams add a log-like append-only structure for cases that need replay, narrowing the gap, but the core models remain distinct.
Where it fits
- Background job and task queues behind web apps.
- Decoupling microservices with request/reply or event notifications.
- Routing-heavy workflows where messages must reach different consumers by rules.