Message Queue Architecture: Kafka vs RabbitMQ vs SQS — Complete Guide
Message Queue Architecture: Kafka vs RabbitMQ vs SQS#
Message queues decouple services, absorb traffic spikes, and enable async processing. But choosing the wrong one creates operational nightmares.
This guide covers when to use each and the patterns that make queues reliable.
Why Message Queues?#
Without a queue:
API → calls Payment Service directly → Payment Service is slow/down → API times out → user sees error
With a queue:
API → publishes to queue → returns 202 Accepted immediately
Queue → Payment Service processes when ready → retries on failure
Benefits: Decoupling, resilience, traffic smoothing, async processing.
The Big Three#
Apache Kafka — Distributed Event Log#
Kafka is a distributed, append-only log. Messages are retained for days/weeks, and multiple consumers can read independently.
Producer → Topic (partitioned log)
Partition 0: [msg1, msg2, msg5, msg8, ...]
Partition 1: [msg3, msg4, msg6, msg9, ...]
Partition 2: [msg7, msg10, ...]
Consumer Group A: reads all partitions (each consumer gets subset)
Consumer Group B: independently reads all partitions (replay!)
Key concepts:
- Topics — named streams of messages
- Partitions — parallelism unit (more partitions = more throughput)
- Consumer groups — each group reads all messages independently
- Offsets — consumers track their position (can replay from any point)
- Retention — messages kept for configurable time (not deleted on read)
Best for: Event streaming, event sourcing, real-time analytics, CDC, log aggregation
RabbitMQ — Traditional Message Broker#
RabbitMQ is a traditional message broker. Messages are delivered to consumers and deleted after acknowledgment.
Producer → Exchange → routes to → Queue A → Consumer A
→ Queue B → Consumer B
Exchange types:
Direct: route by exact routing key
Fanout: broadcast to all bound queues
Topic: route by pattern matching (order.*, payment.#)
Headers: route by message headers
Key concepts:
- Exchanges — route messages to queues based on rules
- Queues — store messages until consumed
- Bindings — rules connecting exchanges to queues
- Acknowledgments — consumer confirms processing
- Dead letter queue — failed messages go here
Best for: Task queues, RPC, complex routing, priority queues
AWS SQS — Managed Queue#
SQS is fully managed — no infrastructure to operate.
Producer → SQS Queue → Consumer polls for messages
→ processes → deletes message
→ visibility timeout prevents double processing
Two types:
- Standard — unlimited throughput, at-least-once delivery, best-effort ordering
- FIFO — exactly-once, strict ordering, 300 TPS (3000 with batching)
Best for: Simple async tasks, Lambda triggers, zero-ops requirement
Comparison#
| Feature | Kafka | RabbitMQ | SQS |
|---|---|---|---|
| Model | Distributed log | Message broker | Managed queue |
| Throughput | 1M+ msg/sec | 30K msg/sec | Unlimited (managed) |
| Ordering | Per-partition | Per-queue | Best-effort (FIFO available) |
| Retention | Days/weeks (configurable) | Until consumed | 14 days max |
| Replay | Yes (seek to any offset) | No | No |
| Delivery | At-least-once (exactly-once with TXN) | At-least-once | At-least-once (exactly-once with FIFO) |
| Routing | Topic-based | Exchange-based (flexible) | Queue-based (simple) |
| Dead letter | Manual (separate topic) | Built-in | Built-in |
| Operations | High (ZooKeeper/KRaft) | Medium | Zero (managed) |
| Cost | Infrastructure | Infrastructure | Pay-per-message |
| Multi-consumer | Yes (consumer groups) | Competing consumers | Competing consumers |
Queue Patterns#
1. Work Queue (Task Distribution)#
Producer → Queue → Consumer 1 (processes task)
→ Consumer 2 (processes task)
→ Consumer 3 (processes task)
Each message goes to ONE consumer. Load balanced across workers.
Use case: Background jobs (email sending, image processing, PDF generation) Best tool: RabbitMQ or SQS
2. Pub/Sub (Fan-Out)#
Producer → Topic/Exchange → Consumer A (gets ALL messages)
→ Consumer B (gets ALL messages)
→ Consumer C (gets ALL messages)
Every message goes to ALL consumers.
Use case: Event notifications (order created → email + analytics + inventory) Best tool: Kafka (consumer groups) or RabbitMQ (fanout exchange)
3. Dead Letter Queue#
Main Queue → Consumer processes → fails 3 times → Dead Letter Queue
↓
Manual review / alert
Failed messages don't block the queue. They're moved aside for investigation.
Always implement DLQs. Without them, poison messages block your entire pipeline.
4. Priority Queue#
Queue with priorities:
Priority 1 (high): [payment_failed, security_alert]
Priority 2 (medium): [order_confirmation]
Priority 3 (low): [marketing_email, analytics_event]
High-priority messages processed first.
Best tool: RabbitMQ (native priority queues)
5. Request-Reply#
Client → Request Queue → Server processes → Reply Queue → Client
(correlation_id: "abc") (correlation_id: "abc")
Synchronous RPC over async queues.
Best tool: RabbitMQ (built-in reply-to header)
6. Saga / Choreography#
Order Service → publishes "OrderCreated" → Kafka
Payment Service → consumes → publishes "PaymentSucceeded"
Inventory Service → consumes → publishes "InventoryReserved"
Shipping Service → consumes → publishes "ShipmentCreated"
Services coordinate via events. No central orchestrator.
Best tool: Kafka (event log for replay and debugging)
Delivery Guarantees#
| Guarantee | Meaning | Trade-off |
|---|---|---|
| At-most-once | Message may be lost, never duplicated | Fastest, unreliable |
| At-least-once | Message delivered 1+ times, may duplicate | Most common, need idempotency |
| Exactly-once | Message delivered exactly once | Slowest, hardest to achieve |
At-least-once + idempotent consumers is the practical standard. True exactly-once is expensive and rarely needed.
Making Consumers Idempotent#
// Before processing, check if already handled
const existing = await db.findOne({ messageId: msg.id });
if (existing) return; // skip duplicate
await processMessage(msg);
await db.insert({ messageId: msg.id, processedAt: new Date() });
Architecture Examples#
E-Commerce Async Processing#
Web App → API → SQS (order queue) → Order Worker
→ Payment Service
→ Inventory Service
→ SQS (email queue) → Email Worker → SendGrid
→ SQS (analytics) → Analytics Worker → ClickHouse
Real-Time Event Platform#
Services → Kafka → Stream Processor (Flink)
→ Real-time dashboards
→ Fraud detection
→ Recommendation engine
→ S3 (archival)
→ Elasticsearch (search index)
Quick Decision#
| Need | Use |
|---|---|
| Event streaming + replay | Kafka |
| Task queue + routing | RabbitMQ |
| Simple async + zero ops | SQS |
| High throughput (1M+ msg/sec) | Kafka |
| Priority queues | RabbitMQ |
| Lambda trigger | SQS |
| Multiple independent consumers | Kafka |
| Complex routing rules | RabbitMQ |
Summary#
- Kafka for event streaming, analytics, and replay
- RabbitMQ for task queues and complex routing
- SQS when you want zero operational overhead
- Always add dead letter queues — poison messages will happen
- At-least-once + idempotency is the practical standard
- Don't over-engineer — SQS handles most async needs
Design message queue architectures at codelit.io — generate interactive diagrams with Kafka, RabbitMQ, and infrastructure exports.
Try it on Codelit
Chaos Mode
Simulate node failures and watch cascading impact across your architecture
Related articles
AI Agent Tool Use Architecture: Function Calling, ReAct Loops & Structured Outputs
6 min read
AI searchAI-Powered Search Architecture: Semantic Search, Hybrid Search, and RAG
8 min read
AI safetyAI Safety Guardrails Architecture: Input Validation, Output Filtering, and Human-in-the-Loop
8 min read
Try these templates
Netflix Video Streaming Architecture
Global video streaming platform with adaptive bitrate, CDN distribution, and recommendation engine.
10 componentsSearch Engine Architecture
Web-scale search with crawling, indexing, ranking, and sub-second query serving.
8 componentsGoogle Search Engine Architecture
Web-scale search with crawling, indexing, PageRank, query processing, ads, and knowledge graph.
10 componentsBuild this architecture
Generate an interactive Message Queue Architecture in seconds.
Try it in Codelit →
Comments