RabbitMQ mu Kafka mı: Doğru Mesaj Kuyruğu Seçimi
Yıllarca her ikisiyle de çalıştım ve şunu söyleyeyim: “RabbitMQ mu Kafka mı?” sorusu aslında yanlış soru. Doğru soru şu: “Benim problemim ne, ve hangi araç o problemi daha az acıyla çözer?” Bu yazıda size teorik karşılaştırma yapmak yerine, gerçek senaryolarda neyin işe yarayıp neyin yaramadığını aktaracağım.
Temel Felsefi Fark
İkisi de mesaj kuyruğu diyoruz ama zihinsel modeller tamamen farklı.
RabbitMQ mesaj odaklı. Bir mesaj gönderirsiniz, tüketilir, gider. “Akıllı broker, aptal consumer” diye özetleyebiliriz. Broker, mesajı doğru yere yönlendirmek için exchange, binding, routing key gibi kavramlar kullanır. Consumer mesajı alır, işler, acknowledge eder, mesaj kuyruktan silinir.
Kafka ise log odaklı. Mesajlar bir append-only log’a yazılır. Consumer’lar bu log’u okur ama mesajlar silinmez (retention policy’ye kadar). “Aptal broker, akıllı consumer” modeli. Broker sadece depolamakla ilgilenir, offset yönetimi consumer’ın sorumluluğundadır.
Bu fark her şeyi belirliyor.
RabbitMQ: Ne Zaman Mantıklı
Klasik Task Queue Senaryoları
Bir e-ticaret sisteminde sipariş geldiğinde şunları yapmanız gerekiyor: fatura kes, kargo bildirimi gönder, SMS at, müşteriye mail at, stok güncelle. Bunların hepsi bağımsız işler ve hata toleranslı olması lazım.
import pika
import json
def send_order_task(order_data):
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
)
channel = connection.channel()
channel.queue_declare(queue='order_processing', durable=True)
channel.basic_publish(
exchange='',
routing_key='order_processing',
body=json.dumps(order_data),
properties=pika.BasicProperties(
delivery_mode=2, # mesajı kalıcı yap
content_type='application/json'
)
)
print(f"Sipariş kuyruğa eklendi: {order_data['order_id']}")
connection.close()
order = {
"order_id": "ORD-12345",
"customer_id": "USR-789",
"items": [{"sku": "PRD-001", "qty": 2}],
"total": 450.00
}
send_order_task(order)
Bu senaryoda RabbitMQ mükemmel çalışır çünkü:
- Her mesajın bir kez işlenmesi yeterli
- İşlem tamamlandığında mesajın saklanmasına gerek yok
- Worker sayısını kolayca ölçekleyebilirsiniz
Dead Letter Queue ile Hata Yönetimi
RabbitMQ’nun bence en güzel özelliklerinden biri Dead Letter Exchange mekanizması. Bir mesaj işlenemezse otomatik olarak başka bir kuyruğa yönlendirilebilir.
import pika
def setup_queues_with_dlx():
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
)
channel = connection.channel()
# Dead letter exchange tanımla
channel.exchange_declare(
exchange='dead_letter_exchange',
exchange_type='direct'
)
# Dead letter kuyruğu
channel.queue_declare(
queue='failed_orders',
durable=True
)
channel.queue_bind(
exchange='dead_letter_exchange',
queue='failed_orders',
routing_key='order_processing'
)
# Ana kuyruğu DLX ile bağla
channel.queue_declare(
queue='order_processing',
durable=True,
arguments={
'x-dead-letter-exchange': 'dead_letter_exchange',
'x-dead-letter-routing-key': 'order_processing',
'x-message-ttl': 30000 # 30 saniye sonra expire olursa DLX'e git
}
)
print("Kuyruklar ve DLX yapılandırıldı")
connection.close()
setup_queues_with_dlx()
Topic Exchange ile Routing
Farklı tiplerdeki bildirimleri farklı consumer’lara yönlendirmek istiyorsanız:
# RabbitMQ Management CLI ile exchange ve binding oluşturma
rabbitmqadmin declare exchange name=notifications type=topic durable=true
rabbitmqadmin declare queue name=email_notifications durable=true
rabbitmqadmin declare queue name=sms_notifications durable=true
rabbitmqadmin declare queue name=push_notifications durable=true
# Binding'leri ekle
rabbitmqadmin declare binding source=notifications
destination=email_notifications
routing_key="notification.*.email"
rabbitmqadmin declare binding source=notifications
destination=sms_notifications
routing_key="notification.*.sms"
# Tüm bildirimleri dinleyen genel kuyruk
rabbitmqadmin declare binding source=notifications
destination=push_notifications
routing_key="notification.#"
Bu yapıyla notification.order.email routing key’iyle gelen mesaj sadece email kuyruğuna, notification.payment.sms sadece SMS kuyruğuna gider. Tek bir mesaj birden fazla kuyruğa fanout edilebilir, bu RabbitMQ’nun güçlü olduğu alan.
Kafka: Ne Zaman Mantıklı
Event Streaming ve Log Aggregation
Kafka’nın gerçek gücü yüksek hacimli event akışlarında ortaya çıkıyor. Bir fintech şirketinde çalıştım, günlük 50 milyon transaction event’i işlememiz gerekiyordu. RabbitMQ denemeleri yapıldı, olmadı. Kafka ile problemin üstüne geldik.
from confluent_kafka import Producer
import json
import time
conf = {
'bootstrap.servers': 'kafka1:9092,kafka2:9092,kafka3:9092',
'acks': 'all', # tüm replica'lar onaylayana kadar bekle
'retries': 3,
'batch.size': 16384,
'linger.ms': 10, # batch için 10ms bekle
'compression.type': 'snappy'
}
producer = Producer(conf)
def delivery_report(err, msg):
if err is not None:
print(f'Mesaj gönderilemedi: {err}')
else:
print(f'Mesaj gönderildi: {msg.topic()} [{msg.partition()}] offset={msg.offset()}')
def send_transaction_event(transaction):
producer.produce(
topic='financial-transactions',
key=str(transaction['user_id']).encode('utf-8'),
value=json.dumps(transaction).encode('utf-8'),
callback=delivery_report
)
producer.poll(0)
# Aynı user_id'ye ait mesajlar aynı partition'a gider
# Bu sayede sıralı işleme garantisi sağlanır
transactions = [
{"tx_id": "TX001", "user_id": 1001, "amount": 150.00, "type": "debit"},
{"tx_id": "TX002", "user_id": 1001, "amount": 75.50, "type": "credit"},
{"tx_id": "TX003", "user_id": 2002, "amount": 200.00, "type": "debit"},
]
for tx in transactions:
send_transaction_event(tx)
producer.flush()
Consumer Group ve Offset Yönetimi
Kafka’nın en kritik konsepti offset. Consumer’lar nerede kaldıklarını kendileri takip ediyor ve geçmişe gidebiliyorlar. Bu RabbitMQ ile mümkün değil.
from confluent_kafka import Consumer, KafkaError
import json
conf = {
'bootstrap.servers': 'kafka1:9092,kafka2:9092,kafka3:9092',
'group.id': 'fraud-detection-service',
'auto.offset.reset': 'earliest',
'enable.auto.commit': False, # manuel commit kontrolü
'max.poll.interval.ms': 300000
}
consumer = Consumer(conf)
consumer.subscribe(['financial-transactions'])
def process_transaction(tx_data):
# Fraud detection mantığı burada
if tx_data['amount'] > 10000:
return False, "Yüksek tutarlı işlem"
return True, None
try:
while True:
msg = consumer.poll(timeout=1.0)
if msg is None:
continue
if msg.error():
if msg.error().code() == KafkaError._PARTITION_EOF:
continue
else:
print(f'Hata: {msg.error()}')
break
tx_data = json.loads(msg.value().decode('utf-8'))
success, reason = process_transaction(tx_data)
if success:
# Başarıyla işlendiyse commit et
consumer.commit(asynchronous=False)
else:
print(f"İşlem reddedildi: {reason}, TX: {tx_data['tx_id']}")
# Offset commit etmiyoruz, bu mesajı tekrar işleyebiliriz
# ya da farklı bir topic'e yönlendirebiliriz
finally:
consumer.close()
Kafka ile Event Sourcing
Kafka’nın log yapısı event sourcing için biçilmiş kaftan. Herhangi bir noktaya geri dönebilirsiniz.
# Kafka topic oluşturma - retention süresini artırarak geçmiş verileri sakla
kafka-topics.sh --create
--bootstrap-server localhost:9092
--topic user-events
--partitions 12
--replication-factor 3
--config retention.ms=604800000
--config cleanup.policy=delete
# Belirli bir offset'ten okumaya başla (debug veya replay senaryoları için)
kafka-console-consumer.sh
--bootstrap-server localhost:9092
--topic user-events
--partition 0
--offset 1000
--max-messages 100
# Consumer group'un lag durumunu kontrol et
kafka-consumer-groups.sh
--bootstrap-server localhost:9092
--group fraud-detection-service
--describe
Bu --describe çıktısı size her partition’da kaç mesaj biriktiğini (lag) gösterir. Monitoring’in temel metriği bu.
Performans ve Operasyonel Gerçekler
Teoride her şey güzel, pratikte farklı hikayeler yaşanıyor.
RabbitMQ operasyonel olarak daha basit. Tek node’da, hatta geliştirme ortamında Docker ile dakikalar içinde çalışır. Cluster kurulumu Kafka’ya kıyasla çok daha az zahmetli. Management UI gerçekten kullanışlı, mesaj akışlarını görsel olarak takip edebilirsiniz.
# RabbitMQ Docker ile hızlı kurulum
docker run -d
--name rabbitmq
-p 5672:5672
-p 15672:15672
-e RABBITMQ_DEFAULT_USER=admin
-e RABBITMQ_DEFAULT_PASS=gizlisifre
rabbitmq:3.12-management
# Cluster durumu kontrolü
rabbitmqctl cluster_status
# Queue istatistikleri
rabbitmqctl list_queues name messages consumers memory
Kafka operasyonel olarak daha ağır. Geleneksel kurulumda ZooKeeper bağımlılığı vardı (KRaft modu ile bu değişiyor), partition sayısı ve replication factor kararları kritik, cluster yönetimi derin bilgi gerektiriyor. Ancak Kafka 2.8+ ile gelen KRaft modu ZooKeeper bağımlılığını ortadan kaldırdı.
# Kafka KRaft modu ile kurulum (ZooKeeper olmadan)
# kraft/server.properties dosyasını düzenle
cat > /opt/kafka/config/kraft/server.properties << 'EOF'
node.id=1
process.roles=broker,controller
listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
inter.broker.listener.name=PLAINTEXT
controller.quorum.voters=1@localhost:9093
log.dirs=/var/kafka/data
num.partitions=6
default.replication.factor=1
EOF
# Cluster UUID oluştur ve format at
KAFKA_CLUSTER_ID="$(kafka-storage.sh random-uuid)"
kafka-storage.sh format -t $KAFKA_CLUSTER_ID
-c /opt/kafka/config/kraft/server.properties
# Kafka'yı başlat
kafka-server-start.sh /opt/kafka/config/kraft/server.properties
Hibrit Mimari: İkisini Birlikte Kullanmak
Gerçek dünya sistemlerinde sıkça karşılaştığım durum şu: bir şirketin hem RabbitMQ hem de Kafka kullandığı, her ikisinin de farklı ihtiyaçları karşıladığı mimari. Bu bir zayıflık değil, akıllı bir mühendislik kararı.
Bir streaming platformu için tasarladığımız mimaride:
- Kafka: Kullanıcı davranış eventleri (tıklama, izleme, arama), analytics pipeline, recommendation engine feed’i
- RabbitMQ: Bildirim görevleri, ödeme işlemleri, e-posta/SMS kuyruğu
Bu ayrımın mantığı şuydu: Kullanıcı davranış verisi yüksek hacimli, replay edilebilir olması gerekiyor, birden fazla downstream servis tüketiyor. Bildirimler ise low-latency, bir kez işlenmeli, karmaşık routing kuralları var.
Karar Kriterleri
Uzun lafın kısası, hangi senaryoda hangisini tercih ederim diye sorarsanız:
RabbitMQ tercih edin eğer:
- Mesajların bir kez işlenmesi yetiyorsa ve geçmişe erişmenize gerek yoksa
- Karmaşık routing mantığı (topic exchange, header exchange) ihtiyacınız varsa
- Request/Reply pattern’i kullanıyorsanız
- Operasyonel sadelik önceliğinizse ve küçük/orta ölçekli ekibiniz varsa
- Mesaj boyutları büyükse (Kafka küçük mesajlar için optimize edilmiş)
- Onlarca değil binlerce mesaj/saniye düzeyinde iş yükünüz varsa
Kafka tercih edin eğer:
- Milyonlarca event/saniye işlemeniz gerekiyorsa
- Veriyi birden fazla consumer group’un bağımsız olarak tüketmesi gerekiyorsa
- Geçmiş verileri yeniden işleme (replay) ihtiyacınız varsa
- Event sourcing veya CQRS pattern’i uyguluyorsanız
- Stream processing (Kafka Streams, ksqlDB) yapacaksanız
- Log aggregation merkezi olarak kullanacaksanız
Her ikisini de kullanmayı düşünün eğer:
- Farklı servislerinizin gerçekten farklı mesajlaşma ihtiyaçları varsa
- Uzun vadeli mimarinizde hem streaming hem task queue’ya ihtiyaç duyuyorsanız
- Ekibiniz her iki sistemi de yönetecek kapasitedeyse
Sonuç
Beş yıl önce bir projede “hadi RabbitMQ yerine Kafka kullanalım, daha modern” kararını gördüm. Sonuç: gereksiz karmaşıklık, operasyonel yük, ve en sonunda geri dönüş. Tam tersi durumu da yaşadım: “RabbitMQ yeter” denilen bir sistemde veri kaybı sorunları ve replay ihtiyacı doğunca Kafka’ya geçmek zorunda kalındı.
Bu araçlar rakip değil, farklı problemlere çözüm üretmek için var. RabbitMQ olgun, kararlı, anlaşılması kolay bir message broker. Kafka ise yüksek hacimli event streaming için endüstri standardı haline gelmiş bir platform.
Teknoloji seçimi her zaman iş gereksinimlerinden başlamalı. “Herkes Kafka kullanıyor” veya “RabbitMQ daha basit” gibi gerekçeler yeterli değil. Yazı yazmak mı istiyorsunuz, yoksa log tutmak mı? Görev dağıtmak mı, yoksa event stream oluşturmak mı? Bu soruların cevabı sizi doğru araca götürür.
Her iki sistemi de küçük scale’de deneyimleyin, production’a almadan önce failure senaryolarını test edin, ve ekibinizin operasyonel kapasitesini göz önünde bulundurun. En iyi mesajlaşma sistemi, ekibinizin uyku kaçırmadan yönetebileceği sistemdir.
