RabbitMQ vs Kafka: Komplexné porovnanie messaging systémov
Po rokoch prevádzkovania RabbitMQ aj Kafka v produkcii je odpoveď jasná: riešia rôzne problémy. Výber medzi nimi nie je o tom, ktorý je “lepší” - je o pochopení ich zásadne odlišných architektúr a use casov.
Toto je praktické porovnanie založené na produkčných skúsenostiach, nie na marketingu dodávateľov.
Testované na: RabbitMQ 3.13 (november 2024), Apache Kafka 3.7 (december 2024)
Rýchly rozhodovací návod
Použite RabbitMQ keď:
- Potrebujete tradičné message queue vzory (work queues, pub/sub, routing)
- Komplexné routingové pravidlá a priority správ sú dôležité
- Nízka latencia pre jednotlivé správy je kritická (<10ms)
- Chcete jednoduchší operačný model (menej pohyblivých častí)
- Message TTL a dead-letter queues sú požiadavky
Použite Kafka keď:
- Potrebujete event streaming a event sourcing
- Vysoká priepustnosť je dôležitejšia ako nízka latencia (>100k msg/s)
- Chcete replay schopnosť a time-travel capability
- Dlhodobé uchovávanie event logu je požadované (dni/týždne/roky)
- Budujete event-driven microservices alebo data pipelines
Fundamentálne rozdiely v architektúre
RabbitMQ: Message Broker
RabbitMQ architektúra (tradičný front):
Producer → Exchange → Queue → Consumer
↓
(Routing Rules)
Kľúčové koncepty:
├─ Správy sa MAŽÚ po spracovaní
├─ Push model: Broker tlačí ku konzumentom
├─ Queue je jednotka paralelizmu
└─ V pamäti s voliteľnou perzistenciou
Životný cyklus správy:
1. Producer pošle na Exchange
2. Exchange routuje do Queue(s) na základe routing key
3. Consumer potvrdí (ACK)
4. Správa sa ZMAŽE z fronty
Kafka: Distribuovaný Commit Log
Kafka architektúra (Event Log):
Producer → Topic → Partitions (na disku) → Consumers
↓
(Immutable Log)
Kľúčové koncepty:
├─ Správy sa UCHOVÁVAJÚ (konfigurovateľná retencia)
├─ Pull model: Konzumenti ťahajú z brokera
├─ Partition je jednotka paralelizmu
└─ Vždy perzistované na disk (sekvenčné zápisy)
Životný cyklus správy:
1. Producer pripojí do partition
2. Správa sa perzistuje na disk
3. Consumer sleduje offset
4. Správa sa UCHOVÁVA pokým nevyprší retenčná politika
Diagram architektúry:
┌─────────────────────────────────────────────────────────┐
│ RabbitMQ: Queue-based (Vymazanie po spracovaní) │
├─────────────────────────────────────────────────────────┤
│ │
│ Producer │
│ │ │
│ ├──> Exchange (fanout/direct/topic/headers) │
│ │ │ │
│ │ ├──> Fronta A ──> Consumer A1 │
│ │ ├──> Fronta B ──> Consumer B1, B2 │
│ │ └──> Fronta C ──> Consumer C1 │
│ │ │
│ └──> Správa vymazaná po ACK │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Kafka: Log-based (Uchovanie do vypršania) │
├─────────────────────────────────────────────────────────┤
│ │
│ Producer │
│ │ │
│ └──> Topic: orders │
│ ├─ Partition 0: [msg1, msg2, msg3, ...] │
│ ├─ Partition 1: [msg4, msg5, msg6, ...] │
│ └─ Partition 2: [msg7, msg8, msg9, ...] │
│ ↓ │
│ Consumer Group A │
│ ├─ Consumer A1 číta P0 │
│ ├─ Consumer A2 číta P1 │
│ └─ Consumer A3 číta P2 │
│ │
│ Consumer Group B (číta VŠETKO nezávisle) │
│ ├─ Consumer B1 číta P0, P1 │
│ └─ Consumer B2 číta P2 │
│ │
│ Správy uchované 7 dní (konfigurovateľné) │
│ │
└─────────────────────────────────────────────────────────┘
Výkonnostné charakteristiky
Throughput benchmark
Testové nastavenie:
- Veľkosť správy: 1KB
- Hardware: 3 brokery, 16 CPU, 64GB RAM, NVMe SSD
- Sieť: 10 Gbps
| Systém | Priepustnosť | Latencia (p50) | Latencia (p99) | CPU využitie | Víťaz |
|---|---|---|---|---|---|
| RabbitMQ | 50k msg/s | 5ms | 25ms | 40% | 🏆 Latencia |
| Kafka | 500k msg/s | 12ms | 45ms | 20% | 🏆 Priepustnosť |
Zhrnutie: Kafka vyhráva v priepustnosti (10x vyššia), RabbitMQ vyhráva v latencii (2-3x nižšia).
Prečo je Kafka rýchlejšia?
Kafka optimalizácie:
├─ Batching: Zoskupuje správy do dávok
├─ Sekvenčný disk I/O: Append-only log (rýchlejší ako náhodný I/O)
├─ Zero-copy: sendfile() systémové volanie (žiadny user-space buffer)
├─ Kompresia: Dávková kompresia (snappy/lz4/gzip)
└─ Page cache: OS-level caching (žiadny tlak na JVM heap)
RabbitMQ bottlenecky:
├─ Per-message routing a ACK overhead
├─ Erlang VM overhead pre kopírovanie správ
├─ Memory-based fronty (GC tlak pri vysokej priepustnosti)
└─ Žiadny batching by default (možno povoliť s pluginmi)
Porovnanie latencie
Scenár: Jednotlivá správa end-to-end latencia
RabbitMQ (push model):
├─ Najlepší prípad: 2-5ms (in-memory fronta, lokálny konzument)
├─ Typický: 5-15ms (perzistentná fronta, vzdialený konzument)
└─ Use case: Nízko-latentné RPC, job queues
Kafka (pull model):
├─ Najlepší prípad: 5-10ms (s linger.ms=0, consumer polling)
├─ Typický: 10-50ms (s batchingom, linger.ms=10)
└─ Use case: Vysokopriepustný event streaming
Víťaz: RabbitMQ pre latenciu jednotlivých správ
Messaging vzory
RabbitMQ Routing vzory
1. Work Queue (Load Balancing)
┌─────────────────────────────────────────┐
│ Work Queue vzor │
├─────────────────────────────────────────┤
│ │
│ Producer ──> Queue ──> Consumer 1 │
│ └───> Consumer 2 │
│ └───> Consumer 3 │
│ │
│ Každá správa doručená JEDNÉMU konzumentovi │
│ (Round-robin by default) │
│ │
└─────────────────────────────────────────┘
Java príklad (RabbitMQ):
// Producer
channel.queueDeclare("work_queue", true, false, false, null);
channel.basicPublish("", "work_queue",
MessageProperties.PERSISTENT_TEXT_PLAIN,
"Task payload".getBytes());
// Consumer
channel.basicQos(1); // Prefetch 1 správa naraz
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
processTask(message);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
};
channel.basicConsume("work_queue", false, deliverCallback, consumerTag -> {});
2. Pub/Sub (Fanout Exchange)
┌──────────────────────────────────────────┐
│ Pub/Sub vzor │
├──────────────────────────────────────────┤
│ │
│ Fanout Exchange │
│ │ │
│ ┌───────────┼───────────┐ │
│ ↓ ↓ ↓ │
│ Queue A Queue B Queue C │
│ ↓ ↓ ↓ │
│ Consumer A Consumer B Consumer C │
│ │
│ Každý konzument dostane VŠETKY správy │
│ │
└──────────────────────────────────────────┘
3. Topic Routing (Pattern Matching)
┌───────────────────────────────────────────────┐
│ Topic Exchange vzor │
├───────────────────────────────────────────────┤
│ │
│ Producer │
│ ├─> "orders.created" ──┐ │
│ ├─> "orders.shipped" ──┤ │
│ └─> "payments.success" ─┤ │
│ │ │
│ Topic Exchange │
│ │ │
│ ┌────────────────────────┼────────┐ │
│ ↓ ↓ ↓ │
│ "orders.*" "orders.created" "*.success"│
│ ↓ ↓ ↓ │
│ Queue A Queue B Queue C │
│ │
│ Wildcards: * (jedno slovo), # (nula alebo viac)│
│ │
└───────────────────────────────────────────────┘
Kafka Partitioning & Consumer Groups
Kafka Partitioning model:
┌──────────────────────────────────────────────────────┐
│ Kafka Topic s Partitions │
├──────────────────────────────────────────────────────┤
│ │
│ Topic: user-events (3 partície) │
│ │
│ Partition 0: [event1, event5, event9, ...] │
│ Partition 1: [event2, event6, event10, ...] │
│ Partition 2: [event3, event7, event11, ...] │
│ │
│ Partitioning stratégia: │
│ ├─ Podľa kľúča: hash(key) % num_partitions │
│ ├─ Round-robin (ak žiadny kľúč) │
│ └─ Vlastný partitioner │
│ │
│ Rovnaký kľúč → rovnaká partícia → záruka poradia! │
│ │
└──────────────────────────────────────────────────────┘
Java príklad (Kafka):
// Producer s key-based partitioning
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// Správy s rovnakým userId idú do rovnakej partície (poradie zachované)
String userId = "user-123";
ProducerRecord<String, String> record = new ProducerRecord<>(
"user-events",
userId, // Kľúč pre partitioning
"{\"event\": \"login\", \"userId\": \"user-123\"}"
);
producer.send(record);
// Consumer Group
Properties consumerProps = new Properties();
consumerProps.put("bootstrap.servers", "localhost:9092");
consumerProps.put("group.id", "analytics-group"); // Consumer group
consumerProps.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
consumerProps.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consumerProps);
consumer.subscribe(Arrays.asList("user-events"));
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf("Partition: %d, Offset: %d, Key: %s, Value: %s%n",
record.partition(), record.offset(), record.key(), record.value());
}
consumer.commitSync(); // Commit offsety
}
Záruky doručenia
RabbitMQ záruky
Záruky doručenia (konfigurovateľné):
1. At-most-once (fire and forget):
├─ Producer: Žiadne potvrdenie
├─ Consumer: Auto-ACK
└─ Riziko: Správy môžu byť stratené
2. At-least-once (default):
├─ Producer: Publisher confirms
├─ Consumer: Manuálny ACK po spracovaní
└─ Riziko: Duplikáty pri zlyhaní
3. Exactly-once:
├─ NIE je podporované natívne
├─ Musí sa implementovať idempotencia v aplikácii
└─ Použiť deduplication plugin alebo app-level dedup
Kafka záruky
Záruky doručenia (konfigurovateľné):
1. At-most-once:
├─ acks=0 (žiadne broker acknowledgment)
└─ Riziko: Správy môžu byť stratené
2. At-least-once (bežné):
├─ acks=1 (leader acknowledgment)
├─ Consumer: Commit offset po spracovaní
└─ Riziko: Duplikáty pri zlyhaní
3. Exactly-once (Kafka 0.11+):
├─ Idempotent producer (enable.idempotence=true)
├─ Transactional producer/consumer
├─ acks=all + min.insync.replicas=2
└─ Skutočné exactly-once v rámci Kafka
Kafka Exactly-Once Semantics (EOS):
├─ Producer: Idempotencia + Transakcie
├─ Consumer: Read committed + Transactional writes
└─ Garantované žiadne duplikáty, žiadne stratené správy
Kafka Exactly-Once príklad:
// Idempotent producer
Properties props = new Properties();
props.put("enable.idempotence", "true"); // Predchádza duplikátom
props.put("acks", "all"); // Čakať na všetky repliky
props.put("retries", Integer.MAX_VALUE);
// Transactional producer
props.put("transactional.id", "my-transactional-id");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
producer.initTransactions();
try {
producer.beginTransaction();
producer.send(new ProducerRecord<>("topic", "key", "value1"));
producer.send(new ProducerRecord<>("topic", "key", "value2"));
producer.commitTransaction(); // Atomický commit
} catch (Exception e) {
producer.abortTransaction(); // Rollback
}
// Consumer: Čítať len committed
Properties consumerProps = new Properties();
consumerProps.put("isolation.level", "read_committed"); // Ignorovať uncommitted
Operačná komplexita
Cluster architektúra
RabbitMQ Cluster:
┌───────────────────────────────────────────┐
│ RabbitMQ Cluster (Mirrored Queues) │
├───────────────────────────────────────────┤
│ │
│ Node 1 (Master) │
│ ├─ Queue A (master) │
│ └─ Queue B (mirror) │
│ │
│ Node 2 (Slave) │
│ ├─ Queue A (mirror) │
│ └─ Queue B (master) │
│ │
│ Node 3 (Slave) │
│ ├─ Queue A (mirror) │
│ └─ Queue B (mirror) │
│ │
│ Výzvy: │
│ ├─ Queue mirroring pridáva latenciu │
│ ├─ Network partitions sú náročné │
│ └─ Rebalancing vyžaduje manuálnu prácu │
│ │
└───────────────────────────────────────────┘
Kafka Cluster:
┌────────────────────────────────────────────┐
│ Kafka Cluster (Distributed Log) │
├────────────────────────────────────────────┤
│ │
│ Broker 1 │
│ ├─ Partition 0 (leader) │
│ ├─ Partition 1 (follower) │
│ └─ Partition 2 (follower) │
│ │
│ Broker 2 │
│ ├─ Partition 0 (follower) │
│ ├─ Partition 1 (leader) │
│ └─ Partition 2 (follower) │
│ │
│ Broker 3 │
│ ├─ Partition 0 (follower) │
│ ├─ Partition 1 (follower) │
│ └─ Partition 2 (leader) │
│ │
│ ZooKeeper / KRaft (metadata) │
│ ├─ Leader election │
│ ├─ Cluster membership │
│ └─ Configuration management │
│ │
│ Výzvy: │
│ ├─ ZooKeeper závislosť (odstraňuje sa) │
│ ├─ Rebalancing môže byť pomalý │
│ └─ Partition reassignment je manuálny │
│ │
└────────────────────────────────────────────┘
Operačné porovnanie
| Aspekt | RabbitMQ | Kafka | Víťaz |
|---|---|---|---|
| Komplexita nastavenia | Jednoduché | Stredné | 🏆 RabbitMQ |
| Monitoring | Vstavaný UI | JMX + 3rd party nástroje | 🏆 RabbitMQ |
| Stratégia škálovania | Vertikálne (pridanie zdrojov) | Horizontálne (pridanie brokerov) | 🏆 Kafka |
| Upgrady | Rolling upgrady | Rolling upgrady | ⚖️ Remíza |
| Backup/Restore | Jednoduché (export fronty) | Náročné (log-based) | 🏆 RabbitMQ |
| Model retencie | Pamäť/disk limit | Čas/veľkosť based | ⚖️ Odlišné |
| Závislosti | Žiadne (Erlang runtime) | ZooKeeper/KRaft + JVM | 🏆 RabbitMQ |
| Učebná krivka | Stredná | Strmá | 🏆 RabbitMQ |
Zhrnutie: RabbitMQ je operačne jednoduchší vo väčšine aspektov. Kafka vyžaduje viac expertízy, ale škáluje sa horizontálne lepšie.
Use cases
Kedy vyhráva RabbitMQ
1. Task Queues / Job Processing
├─ Príklad: Spracovanie obrázkov, posielanie emailov
├─ Prečo: Work queue vzor, automatické load balancing
└─ Vzor: Producer → Queue → Worker pool
2. RPC (Request/Reply)
├─ Príklad: Microservice synchrónne volania
├─ Prečo: Correlation ID, reply-to queue
└─ Vzor: Client → Request queue → Server → Reply queue
3. Priority Queues
├─ Príklad: Vysoko-prioritné alerty vs nízko-prioritné logy
├─ Prečo: Natívna podpora priority queue (0-255)
└─ Vzor: Správy s priority header
4. Komplexný Routing
├─ Príklad: Multi-tenant message routing
├─ Prečo: Topic exchanges, header exchanges, routing keys
└─ Vzor: Exchange routing logika
5. Dead-Letter Queues (DLQ)
├─ Príklad: Spracovanie zlyhavších správ
├─ Prečo: Natívne DLQ s TTL a retry policies
└─ Vzor: Hlavná fronta → DLQ po max retries
Kedy vyhráva Kafka
1. Event Sourcing
├─ Príklad: Audit log, order history
├─ Prečo: Immutable log, replay capability
└─ Vzor: Append-only event log
2. Stream Processing
├─ Príklad: Real-time analytika, fraud detection
├─ Prečo: Kafka Streams, ksqlDB
└─ Vzor: Topic → Stream processor → Aggregated topic
3. Log Aggregation
├─ Príklad: Centralizované logovanie, metriky
├─ Prečo: Vysoká priepustnosť, dlhá retencia
└─ Vzor: Application logs → Kafka → Elasticsearch
4. CDC (Change Data Capture)
├─ Príklad: Database replikácia, cache invalidation
├─ Prečo: Debezium + Kafka Connect
└─ Vzor: DB → Kafka → Downstream consumers
5. Data Pipelines
├─ Príklad: Data warehouse ingestion
├─ Prečo: Kafka Connect, Schema Registry
└─ Vzor: Source → Kafka → Sink connectors
Záver
Po rokoch v produkcii moje odporúčanie:
RabbitMQ je tradičný message broker - skvelý pre fronty, routing a RPC. Ak budujete spracovanie úloh, job queues alebo potrebujete komplexný routing, RabbitMQ je jednoduchší a výkonnejší pre nízko-latentné workloady.
Kafka je distribuovaný commit log - navrhnutý pre event streaming, vysokú priepustnosť a data pipelines. Ak robíte event sourcing, stream processing alebo potrebujete prehrávať eventy, Kafka je správny nástroj.
Pre nové projekty:
- Začnite s RabbitMQ ak potrebujete message queue
- Začnite s Kafka ak potrebujete event log
Pre existujúce projekty:
- Nemigrujte pokiaľ nemáte jasné problémy
- Oba sú zrelé, overené systémy
Správna voľba závisí od vášho use case, nie od toho, ktorá technológia je “novšia” alebo “škálovateľnejšia.”
Rýchla referencia
Rozhodovacia matica
| Požiadavka | RabbitMQ | Kafka | Víťaz |
|---|---|---|---|
| Priepustnosť | 50k msg/s | 500k+ msg/s | 🏆 Kafka |
| Latencia | <10ms | 10-50ms | 🏆 RabbitMQ |
| Retencia správ | Do ACK | Dni/týždne | ⚖️ Odlišné |
| Replay schopnosť | ❌ Nie | ✅ Áno | 🏆 Kafka |
| Routing vzory | ✅ Vynikajúce (4 typy) | 🟡 Základné (key-based) | 🏆 RabbitMQ |
| Priority queues | ✅ Áno (0-255) | ❌ Nie | 🏆 RabbitMQ |
| Exactly-Once doručenie | 🟡 App-level | ✅ Natívne | 🏆 Kafka |
| Operačný overhead | 🟢 Nízky | 🟡 Stredný | 🏆 RabbitMQ |
| Učebná krivka | 🟡 Stredná | 🔴 Strmá | 🏆 RabbitMQ |
| Primárny use case | Task queues, RPC | Event log, streaming | ⚖️ Odlišné |
Rýchle rozhodnutie:
- Zvoľte RabbitMQ pre: Spracovanie úloh, job queues, komplexný routing, nízka latencia
- Zvoľte Kafka pre: Event sourcing, stream processing, vysoká priepustnosť, data pipelines
Citovať túto príručku
Ak odkazujete na túto príručku, prosím odkazujte na pôvodnú URL a uveďte autora.