RabbitMQ vs Apache Kafka: Message Brokers Compared
RabbitMQ is an AMQP message queue broker while Apache Kafka is a distributed commit log — two approaches to messaging and event streaming.
At a Glance
| Feature | RabbitMQ | Apache Kafka |
|---|---|---|
| Model | AMQP queue (push-based) | Commit log (pull-based) |
| Throughput | Thousands of messages/s | Millions of messages/s |
| Routing | Exchanges + bindings (topic, direct, fanout, headers) | Topics with partitions |
| Ordering | Guaranteed per queue | Guaranteed per partition |
| Persistence | Optional (acks, durable queues) | Configurable (flush to disk, replication) |
| Replay | No — consumed messages are removed | Yes — offset-based replay |
| Delivery Semantics | At-most-once, at-least-once | At-most-once, at-least-once, exactly-once |
| Protocol | AMQP 0-9-1, AMQP 1.0, MQTT, STOMP | Custom binary protocol over TCP |
| Latency | Sub-millisecond (low) | Low (configurable batch/flush) |
| Message Retention | Until consumed (or TTL) | Configurable retention (time or size) |
Key Differences
- Message Model: RabbitMQ is a queue broker — messages are pushed to consumers and removed once acknowledged. Kafka is a commit log — messages are stored durably and consumers read at their own pace by tracking offsets. This fundamental difference determines everything else: RabbitMQ excels at work distribution; Kafka excels at event streaming and replay.
- Performance: Kafka is designed for high throughput — millions of messages per second are typical. It achieves this through sequential disk I/O, batching, partitioning, and zero-copy optimization. RabbitMQ handles thousands per second — adequate for most business applications but not for high-volume data pipelines.
- Routing: RabbitMQ’s exchange types (direct, topic, fanout, headers) give you fine-grained control over message routing. Kafka uses topics with partitions — consumers subscribe to topics and can use consumer groups for workload distribution. Kafka has no built-in routing logic; messages with the same key go to the same partition.
- Replay: This is Kafka’s killer feature — consumers can replay messages from any offset at any time (within the retention window). RabbitMQ removes messages after consumption — replay requires custom logic (republish, dead-letter queues). Kafka’s log-based storage makes replay natural.
- Ordering: RabbitMQ guarantees message order within a single queue. Kafka guarantees order within a partition. With multiple consumers in RabbitMQ or multiple partitions in Kafka, ordering across the system is not guaranteed without intentional design.
When to Choose RabbitMQ
Choose RabbitMQ when you need flexible routing, task distribution, and low latency. RabbitMQ is ideal for microservices communication, background job processing (send email, resize image), RPC-style request/reply patterns, and applications where each message should be processed once by one consumer. RabbitMQ’s exchange types make complex routing scenarios — like sending to specific queues based on message headers — straightforward. RabbitMQ’s management UI makes monitoring and debugging easy for operations teams.
When to Choose Apache Kafka
Choose Apache Kafka when you need event streaming, data pipelines, and message replay. Kafka is the standard for change data capture (CDC), log aggregation, metrics collection, and event sourcing. Kafka’s log-based model enables stream processing (via Kafka Streams or ksqlDB) where you compute results over windows of events. Kafka’s retention-based storage means consumers can process at their own pace — new services can consume historical data from day one. At DodaTech, Kafka powers the real-time threat intelligence pipeline for Durga Antivirus Pro — processing millions of threat signals per second.
Side by Side Code Example: Publish and Consume Messages
RabbitMQ
// Publisher
const amqp = require("amqplib");
const conn = await amqp.connect("amqp://localhost");
const channel = await conn.createChannel();
const queue = "orders";
await channel.assertQueue(queue, { durable: true });
channel.sendToQueue(queue, Buffer.from(JSON.stringify({ orderId: 123 })));
// Consumer
channel.consume(queue, (msg) => {
const order = JSON.parse(msg.content.toString());
console.log("Processing order:", order.orderId);
channel.ack(msg); // Acknowledge — message is removed
});Apache Kafka
// Publisher (Producer)
const { Kafka } = require("kafkajs");
const kafka = new Kafka({ clientId: "app", brokers: ["localhost:9092"] });
const producer = kafka.producer();
await producer.connect();
await producer.send({
topic: "orders",
messages: [{ value: JSON.stringify({ orderId: 123 }) }],
});
// Consumer
const consumer = kafka.consumer({ groupId: "order-processor" });
await consumer.connect();
await consumer.subscribe({ topic: "orders", fromBeginning: true });
await consumer.run({
eachMessage: async ({ message }) => {
const order = JSON.parse(message.value.toString());
console.log("Processing order:", order.orderId);
// Offset is committed automatically — message stays in log
},
});Both publish to an “orders” destination and consume messages. RabbitMQ removes the message after acknowledgment; Kafka keeps it in the log. The RabbitMQ consumer must call ack() explicitly; Kafka’s consumer commits the offset automatically.
FAQ
Related Comparisons
SQL vs NoSQL for database choices. Monolith vs Microservices for architectural patterns.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro