Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

RabbitMQ & Traditional Queues

Traditional message broker with flexible routing

RabbitMQ is a traditional message broker implementing the AMQP (Advanced Message Queuing Protocol) standard.

Diagram
  1. Exchange - Receives messages, routes to queues
  2. Queue - Stores messages
  3. Binding - Connection between exchange and queue
  4. Routing Key - Message attribute used for routing

Routes to queue with matching routing key.

Diagram

Use case: Point-to-point messaging, task queues

Routes based on pattern matching (wildcards).

Diagram

Patterns:

  • * - Matches one word
  • # - Matches zero or more words

Use case: Categorized messages, event routing

Broadcasts to all bound queues (ignores routing key).

Diagram

Use case: Pub-sub, notifications, cache invalidation

Routes based on message headers (ignores routing key).

Use case: Complex routing logic


"rabbitmq_producer.py
import pika
import json
class RabbitMQProducer:
"""RabbitMQ producer"""
def __init__(self, host='localhost'):
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(host=host)
)
self.channel = self.connection.channel()
def setup_exchange(self, exchange_name: str, exchange_type: str = 'direct'):
"""Declare exchange"""
self.channel.exchange_declare(
exchange=exchange_name,
exchange_type=exchange_type,
durable=True # Survive broker restart
)
def publish(self, exchange: str, routing_key: str, message: dict):
"""Publish message"""
self.channel.basic_publish(
exchange=exchange,
routing_key=routing_key,
body=json.dumps(message),
properties=pika.BasicProperties(
delivery_mode=2, # Make message persistent
content_type='application/json'
)
)
print(f"Published to {exchange} with key {routing_key}")
def close(self):
"""Close connection"""
self.connection.close()
# Usage
producer = RabbitMQProducer()
producer.setup_exchange('orders', 'direct')
producer.publish('orders', 'order.created', {
'order_id': 123,
'user_id': 456,
'amount': 99.99
})
producer.close()
"rabbitmq_consumer.py
import pika
import json
class RabbitMQConsumer:
"""RabbitMQ consumer"""
def __init__(self, host='localhost'):
self.connection = pika.BlockingConnection(
pika.ConnectionParameters(host=host)
)
self.channel = self.connection.channel()
def setup_queue(self, queue_name: str, durable: bool = True):
"""Declare queue"""
self.channel.queue_declare(
queue=queue_name,
durable=durable # Survive broker restart
)
def bind_queue(self, queue: str, exchange: str, routing_key: str):
"""Bind queue to exchange"""
self.channel.queue_bind(
queue=queue,
exchange=exchange,
routing_key=routing_key
)
def consume(self, queue: str, handler, auto_ack: bool = False):
"""Consume messages"""
def callback(ch, method, properties, body):
try:
message = json.loads(body)
# Process message
handler(message)
# Acknowledge message
if not auto_ack:
ch.basic_ack(delivery_tag=method.delivery_tag)
except Exception as e:
print(f"Error processing message: {e}")
# Reject and requeue
if not auto_ack:
ch.basic_nack(
delivery_tag=method.delivery_tag,
requeue=True
)
# Set prefetch (how many unacked messages per consumer)
self.channel.basic_qos(prefetch_count=1)
# Start consuming
self.channel.basic_consume(
queue=queue,
on_message_callback=callback,
auto_ack=auto_ack
)
print(f"Consuming from {queue}...")
self.channel.start_consuming()
def close(self):
"""Close connection"""
self.connection.close()
# Usage
def handle_order(message):
print(f"Processing order: {message['order_id']}")
# Process order...
consumer = RabbitMQConsumer()
consumer.setup_queue('order-processor', durable=True)
consumer.bind_queue('order-processor', 'orders', 'order.created')
consumer.consume('order-processor', handle_order, auto_ack=False)

Critical for reliable message processing.

# Message removed immediately when delivered
channel.basic_consume(queue='orders', on_message_callback=callback, auto_ack=True)

Problem: If consumer crashes, message lost!

# Message removed only after ack
def callback(ch, method, properties, body):
process_message(body)
ch.basic_ack(delivery_tag=method.delivery_tag) # Acknowledge
channel.basic_consume(queue='orders', on_message_callback=callback, auto_ack=False)

Benefits:

  • ✅ Message redelivered if consumer crashes
  • ✅ Can reject and requeue on error
  • ✅ Reliable processing

FeatureRabbitMQKafka
ModelTraditional brokerStreaming platform
Message RetentionRemoved after consumptionRetained (configurable)
RoutingFlexible (exchanges)Simple (topics/partitions)
OrderingPer queuePer partition
ThroughputGoodExcellent
Use CaseTask queues, RPCEvent streaming, logs

Choose RabbitMQ when:

  • Need flexible routing
  • Task queues
  • Message removed after processing
  • Complex routing logic

Choose Kafka when:

  • Event streaming
  • High throughput
  • Message retention needed
  • Replay capability needed

🔄 Exchanges Route

Exchanges route messages to queues based on routing keys. Direct, topic, fanout, headers.

✅ Manual Ack

Use manual acknowledgment for reliability. Auto-ack removes messages immediately (risky).

💾 Durability

Make queues/exchanges/messages durable to survive broker restart. Critical for production.

📊 Flexible Routing

RabbitMQ’s flexible routing (exchanges) makes it great for complex routing scenarios.