import ComparisonTable from ’../../components/ComparisonTable.astro’;
Message brokers are fundamental to distributed systems — enabling async communication, decoupling services, and buffering load spikes. Kafka and RabbitMQ solve similar problems with very different architectures.
Quick Verdict
Choose Kafka if: You need high-throughput event streaming, want to replay events, are building data pipelines, or need messages to be stored durably for hours/days/forever.
Choose RabbitMQ if: You need traditional task queues, complex routing, lower-latency message delivery, or have smaller-scale messaging needs.
Architecture Difference
Kafka: Log-based
Kafka stores messages in an immutable, ordered log. Consumers read from any position in the log and maintain their own offset.
Partition 0: [msg1] [msg2] [msg3] [msg4] [msg5] → offset 5
↑
Consumer A at offset 4 (unread: msg4, msg5)
Consumer B at offset 2 (unread: msg3, msg4, msg5)
Consumer C at offset 5 (fully caught up)
Messages are NOT deleted when consumed. They’re retained based on time or size policies.
RabbitMQ: Queue-based
RabbitMQ uses classic message queuing — messages are pushed to consumers and deleted after acknowledgment.
Exchange → Queue → [msg1] [msg2] [msg3] [msg4]
↑
Consumer pops msg, processes, ACKs
Message deleted from queue
Messages exist until consumed (or TTL expires).
Feature Comparison
<ComparisonTable headers={[“Feature”, “Apache Kafka”, “RabbitMQ”]} rows={[ [“Throughput”, “Millions msg/sec”, “Thousands-100K msg/sec”], [“Message retention”, “Configurable (hours to forever)”, “Until consumed”], [“Consumer groups”, “Multiple independent consumers”, “Competing consumers (round-robin)”], [“Message replay”, “Yes (seek to any offset)”, “No”], [“Routing”, “By topic/partition”, “Complex (exchanges, bindings)”], [“Latency”, “Low (5-10ms typical)”, “Very low (1-5ms typical)”], [“Ordering”, “Per partition”, “Per queue”], [“Protocol”, “Kafka protocol”, “AMQP, MQTT, STOMP”], [“Management UI”, “Kafka UI (third-party)”, “Built-in management UI”], [“Managed options”, “Confluent, MSK, Aiven”, “CloudAMQP, AmazonMQ”], ]} />
Kafka Producers and Consumers
from confluent_kafka import Producer, Consumer, KafkaError
# Producer
producer = Producer({'bootstrap.servers': 'localhost:9092'})
def delivery_report(err, msg):
if err:
print(f'Delivery failed: {err}')
else:
print(f'Delivered to {msg.topic()}[{msg.partition()}] @ {msg.offset()}')
# Produce messages
for i in range(1000):
producer.produce(
topic='user-events',
key=f'user-{i % 100}', # Key determines partition
value=f'{{"event": "page_view", "user_id": {i}, "page": "/home"}}',
callback=delivery_report,
)
producer.flush()
# Consumer
consumer = Consumer({
'bootstrap.servers': 'localhost:9092',
'group.id': 'analytics-service', # Consumer group
'auto.offset.reset': 'earliest', # Start from beginning if no committed offset
})
consumer.subscribe(['user-events'])
while True:
msg = consumer.poll(timeout=1.0)
if msg is None:
continue
if msg.error():
if msg.error().code() == KafkaError._PARTITION_EOF:
continue
break
# Process message
print(f'Received: {msg.value().decode()} from partition {msg.partition()}')
consumer.commit() # Manual commit for at-least-once delivery
RabbitMQ Producers and Consumers
import pika
import json
# Producer
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
# Declare exchange and queue
channel.exchange_declare(exchange='orders', exchange_type='direct', durable=True)
channel.queue_declare(queue='order-processing', durable=True)
channel.queue_bind(queue='order-processing', exchange='orders', routing_key='new')
# Publish message
message = {'order_id': '123', 'total': 99.99, 'status': 'pending'}
channel.basic_publish(
exchange='orders',
routing_key='new',
body=json.dumps(message),
properties=pika.BasicProperties(
delivery_mode=2, # Persistent (survives broker restart)
content_type='application/json',
)
)
connection.close()
# Consumer
def process_order(ch, method, properties, body):
order = json.loads(body)
print(f"Processing order {order['order_id']}")
# Do work...
ch.basic_ack(delivery_tag=method.delivery_tag) # Acknowledge
# If not acked: message requeued after consumer disconnect
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.basic_qos(prefetch_count=1) # Process one at a time
channel.basic_consume(queue='order-processing', on_message_callback=process_order)
channel.start_consuming()
RabbitMQ’s Routing Power
RabbitMQ’s exchange types enable sophisticated routing:
Exchange Types:
- Direct: Route by exact routing key
- Fanout: Broadcast to all bound queues
- Topic: Route by pattern (user.# matches user.created, user.deleted.account)
- Headers: Route by message headers (not key)
Example: Order notification system
Exchange: orders-exchange (topic)
Queue: email-service
Binding: order.confirmed.* (all confirmed orders)
Queue: inventory-service
Binding: order.# (all order events)
Queue: fraud-service
Binding: order.large.* (custom routing by business logic)
This level of routing sophistication doesn’t exist in Kafka natively — you’d need separate topics.
When Kafka Wins
Event sourcing: Kafka’s log is perfect for event sourcing — every state change as an event, replayable, immutable.
Real-time analytics: High-throughput event streams for Flink, Spark, or Kafka Streams processing.
Multiple consumers, same events: Order events consumed by billing, inventory, email, and analytics — each independently, at their own pace.
Audit logging: Retention of all events for compliance or debugging.
Data pipelines: Moving data between systems reliably at scale (Kafka Connect).
When RabbitMQ Wins
Task queues: Background job processing (resize images, send emails, generate reports) — RabbitMQ’s work queue pattern is simpler.
Low latency delivery: RabbitMQ’s push model can deliver faster for real-time applications.
Complex routing: Multiple services subscribing to different subsets of messages.
Small scale: If you’re not at Kafka’s scale, RabbitMQ is simpler to operate.
Legacy integration: AMQP is widely supported across languages and frameworks.
Managed Services
Kafka:
- Confluent Cloud — Full Kafka + Flink integration, enterprise features
- Amazon MSK — AWS-managed Kafka, good AWS integration
- Aiven for Kafka — Multi-cloud, competitive pricing
- Redpanda — Kafka-compatible, faster, simpler operations
RabbitMQ:
- CloudAMQP — Fully managed, 5 plans including free
- Amazon MQ — AWS-managed RabbitMQ (and ActiveMQ)
Bottom Line
Kafka for event streaming, high-throughput messaging, and architectures where multiple consumers need independent access to the same events. RabbitMQ for traditional task queues, complex routing requirements, and simpler operational needs. At modern cloud scale, Kafka is increasingly the default for new architectures — but RabbitMQ remains excellent for simpler message passing patterns.