Kafka vs RabbitMQ: Which Message Broker Should You Actually Pick?
Why is every "Kafka vs RabbitMQ" article just a feature table and some made-up throughput numbers?
You don't pick a broker from a spreadsheet. You pick it because one of them matches how your data actually flows and the other one fights you the entire time. The difference isn't speed or popularity. It's architecture. And once you see it, the rest of the decision becomes obvious.
🧠One is a log, the other is a router
Kafka is a distributed append-only commit log. Producers write messages to the end of a partition. That's it. Messages sit there, indexed by offset, until a retention policy (time or size) eventually cleans them up. Nobody deletes them when they're "done."
Consumers pull. They track their own position in the log by committing offsets back to Kafka. The broker doesn't know or care whether you've processed a message. It just stores segments and serves reads.
RabbitMQ is the opposite. It's a smart broker. Messages arrive at an exchange, get routed through bindings to one or more queues, and the broker pushes them to consumers. When a consumer sends an ack, the message is gone. Deleted. The broker owns delivery state.
So Kafka says: "here's the log, read wherever you want." RabbitMQ says: "tell me where things should go and I'll deliver them." Everything else follows from this split.
âš¡ RabbitMQ's exchange model
Kafka has topics and partitions. You publish to a topic, and the partition key decides which partition it lands in. Simple. But there's no broker-side routing logic. Consumers get everything on their assigned partitions and filter client-side if needed.
RabbitMQ gives you four exchange types, each with different routing rules:
| Exchange type | What it does |
|---|---|
| Direct | Routes to queues whose binding key exactly matches the message's routing key |
| Topic | Wildcard matching on routing key patterns (order.*.created, #.error) |
| Fanout | Broadcasts to every bound queue. No filtering. |
| Headers | Matches on message header attributes instead of routing key |
This means the broker itself decides who gets what. You can have one publisher sending order events and the broker splits them across an invoice queue, a notification queue, and an analytics queue based on routing keys. No consumer-side logic. No duplicated subscriptions.
If your system needs complex message routing, RabbitMQ handles it natively. With Kafka, you'd either use multiple topics or build filtering into every consumer. Not the end of the world, but it's work you don't have to do with RabbitMQ.
Replay vs delete-on-ack
This is where the architectural choice really bites.
Kafka retains messages. Period. A consumer can rewind to offset zero and reprocess everything from the beginning. Deployed a bug that corrupted downstream data? Reset the consumer group offset and replay. Need a second service to read the same events independently? Just add another consumer group. The data's still there.
I wrote about how consumer groups and partition assignment work in a previous post, so I won't re-explain the mechanics here.
RabbitMQ's classic and quorum queues delete messages on ack. Gone forever. If you need reprocessing, you're out of luck unless you built your own archival system. Dead-letter exchanges catch rejected messages, but that's error handling, not replay.
But here's the thing. RabbitMQ added Streams back in version 3.9. Streams are an append-only replicated log with offset-based consumption. Basically Kafka semantics inside RabbitMQ. They support replay, time-based seeking, and fan-out without re-delivery. So the line is blurring. Still, if replay is your primary use case, Kafka was built for it from day one.
Ordering guarantees
Kafka: Total order within a partition. Messages sharing the same key hash to the same partition, so per-key ordering is strict. Want ordered processing across multiple consumers? That's what consumer groups give you, with each partition going to exactly one consumer.
RabbitMQ: FIFO within a single queue. Clean and simple. But the moment you add competing consumers (multiple consumers on one queue for parallelism), messages get dispatched round-robin and ordering breaks. You either accept that or run single-consumer queues, which limits throughput.
Delivery semantics
Both default to at-least-once delivery. Your consumer might see the same message twice if something crashes mid-processing. But how they handle stronger guarantees differs:
| At-least-once | Effectively-once | |
|---|---|---|
| Kafka |
acks=all + retries (default) |
Idempotent producer (default since Kafka 3.0) deduplicates retries via sequence numbers. Transactions give atomic read-process-write across partitions. |
| RabbitMQ | Publisher confirms + consumer ack | No built-in exactly-once. Deduplication is your problem. Quorum queues guarantee replication, but not dedup. |
So if you need end-to-end exactly-once (or close to it), Kafka has first-party support. With RabbitMQ you're implementing idempotency yourself. Not impossible, just extra work.
Ops in 2025-2026
A quick update on where both projects stand operationally, because a lot of older comparison posts are outdated:
Kafka 4.0 (March 2025) fully removed ZooKeeper. It's gone. KRaft mode is the only option now. This cuts a huge operational dependency. No more separate ZK cluster to babysit. Easier to deploy, fewer moving parts.
RabbitMQ 4.0 removed classic mirrored queues entirely. Quorum queues (Raft-based replication) are the only replicated queue type going forward. Better consistency, better throughput under replication. If you're reading guides that mention ha-mode policies, they're obsolete.
📌 I'm not quoting throughput numbers
Every comparison post throws around "Kafka does 2 million msgs/sec" or "RabbitMQ maxes out at 50K." I'm not doing that.
Why? Because those numbers mean nothing without context. Message size, persistence settings, replication factor, ack mode, batch size, hardware, network. Change any one variable and the number changes by 10x. A benchmark where Kafka batches 1KB messages with acks=1 and RabbitMQ uses durable quorum queues with per-message confirms isn't a comparison. It's fiction dressed up as data.
Here's what I'll say: Kafka is architecturally optimized for throughput (sequential disk writes, zero-copy with sendfile, batching). RabbitMQ optimizes for per-message routing flexibility and low latency at moderate scale. That's the shape of it. Actual numbers depend entirely on your deployment.
So which one do you pick?
This is the decision guide. Be honest about your actual requirements.
Pick Kafka when you need event streaming, event sourcing, or replay. When you have high throughput requirements (think hundreds of thousands of messages per second). When multiple independent services need to consume the same data stream. Log aggregation, change data capture, CQRS. All Kafka territory.
Pick RabbitMQ when you need complex routing logic at the broker level. When you're doing request-reply patterns or RPC. When you need per-message priority, TTL, or dead-letter handling. When you want polyglot protocol support (AMQP, MQTT, STOMP) without running separate brokers. Moderate scale, task queues, workflow orchestration.
Pick neither when you don't want to operate a message broker at all. If your system has low throughput, simple fan-out, and you're running on AWS anyway, just use SQS. Or SNS+SQS for pub/sub. Zero cluster management, no replication to configure, no disk monitoring. I talked about how API gateways sit in front of these services if you're building event-driven architectures on managed infrastructure.
Honestly, for a lot of teams shipping their first async system, SQS is the right answer. No shame in it. You can always migrate to Kafka or RabbitMQ when you outgrow it. Operating a broker cluster before you need one is just yak shaving.
Where else to find me
My other posts, plus what I'm building right now, are at arnavsharma.dev.
Top comments (0)