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.