Message Queue Nedir? Kafka ve RabbitMQ ile Asenkron Mimari
Bir e-ticaret sitesinde sipariş veriyorsunuz. Saniyeler içinde onay emaili geliyor, kargo firması bilgilendiriliyor, stok güncelleniyor, analitik sistemi kayıt alıyor.
Tüm bunlar aynı HTTP isteğinin içinde senkron olarak mı oluyor? Hayır. Böyle tasarlansaydı sipariş endpoint'i 10 saniye sürerdi ve herhangi bir servis çökünce sipariş de başarısız olurdu.
Çözüm: message queue.
Senkron vs Asenkron İletişim
Senkron iletişimde A servisi B servisini çağırır ve yanıt gelene kadar bekler. Basit ama kırılgan: B yavaşlarsa A yavaşlar, B çökerse A çöker.
Senkron:
OrderService → EmailService (bekle...) 2 sn
→ SMSService (bekle...) 1 sn
→ InventoryService (bekle...) 0.5 sn
Toplam: 3.5 saniye, herhangi biri çökerse işlem başarısız
Asenkron iletişimde A servisi bir mesaj bırakır ve işine devam eder. B servisi hazır olduğunda mesajı alır ve işler.
Asenkron:
OrderService → Queue: "order.placed" (0.01 sn)
← Kullanıcıya anında yanıt
(Arka planda, bağımsız olarak)
EmailService ← mesajı aldı, emaili gönderdi
SMSService ← mesajı aldı, SMS gönderdi
InventoryService ← mesajı aldı, stoku güncelledi
Sipariş servisi 10ms'de tamamlandı. Diğer servisler bağımsız, paralel çalışıyor. Biri çökse diğerleri etkilenmiyor, mesaj queue'da bekliyor.
Message Queue'nun Temel Kavramları
Producer (Üretici): Mesaj üreten ve queue'ya gönderen taraf. Sipariş servisi bir "order.placed" eventi üretir.
Consumer (Tüketici): Queue'dan mesaj alan ve işleyen taraf. Email servisi, SMS servisi, stok servisi birer consumer.
Queue/Topic: Mesajların bekletildiği yapı. RabbitMQ'da "queue", Kafka'da "topic" denir.
Message: Queue'ya gönderilen veri paketi.
{ "eventType": "order.placed", "orderId": "ord-789", "userId": "usr-123", "items": [ { "productId": "prod-456", "quantity": 2, "price": 49.99 } ], "totalAmount": 99.98, "timestamp": "2025-03-05T10:30:00Z" }
RabbitMQ: Geleneksel Message Broker
RabbitMQ, AMQP protokolünü kullanan olgun ve güvenilir bir message broker'dır. Mesaj yönlendirme konusunda son derece esnektir.
Exchange: Producer mesajı doğrudan queue'ya değil exchange'e gönderir. Exchange, mesajı kurallara göre doğru queue'lara yönlendirir.
import amqp from "amqplib"; // Producer: Sipariş oluşturulduğunda mesaj gönder async function publishOrderEvent(order) { const connection = await amqp.connect("amqp://localhost"); const channel = await connection.createChannel(); await channel.assertExchange("order_events", "topic", { durable: true }); channel.publish( "order_events", "order.placed", Buffer.from(JSON.stringify(order)), { persistent: true } ); } // Consumer: Email servisi async function startEmailConsumer() { const connection = await amqp.connect("amqp://localhost"); const channel = await connection.createChannel(); await channel.assertExchange("order_events", "topic", { durable: true }); await channel.assertQueue("email_service_queue", { durable: true }); await channel.bindQueue("email_service_queue", "order_events", "order.*"); channel.prefetch(1); channel.consume("email_service_queue", async (msg) => { if (!msg) return; const order = JSON.parse(msg.content.toString()); try { await emailService.sendOrderConfirmation(order); channel.ack(msg); // Basarili: mesaji queue'dan sil } catch (error) { channel.nack(msg, false, true); // Basarisiz: tekrar kuyruga al } }); }
Acknowledgment (ACK): Consumer mesajı işledikten sonra broker'a "aldım, sil" der. İşlem başarısız olursa nack ile mesaj tekrar queue'ya döner. Veri kaybı olmaz.
Kafka: Yüksek Hacimli Event Streaming
Apache Kafka, RabbitMQ'dan farklı bir paradigmada çalışır. Kafka bir mesaj kuyruğu değil, dağıtık event log'dur.
En kritik fark: Kafka'da consumer mesajı aldıktan sonra mesaj silinmez. Mesajlar belirli bir süre (varsayılan 7 gün) disk üzerinde tutulur. Yeni bir consumer eklendiğinde geçmiş mesajları da okuyabilir.
RabbitMQ: Mesaj alındı → Silindi
Kafka: Mesaj alındı → Hâlâ disk üzerinde (retention period boyunca)
Topic: Mesajların kategorize edildiği kanal.
Partition: Her topic birden fazla partition'a bölünür. Paralel okuma ve yüksek throughput için.
Consumer Group: Aynı topic'i farklı consumer group'lar bağımsız okuyabilir.
import { Kafka } from "kafkajs"; const kafka = new Kafka({ clientId: "order-service", brokers: ["kafka:9092"] }); // Producer const producer = kafka.producer(); async function publishEvent(topic, event) { await producer.connect(); await producer.send({ topic, messages: [{ key: event.orderId, value: JSON.stringify(event) }] }); } // Consumer - Email Servisi const consumer = kafka.consumer({ groupId: "email-service" }); async function startConsumer() { await consumer.connect(); await consumer.subscribe({ topic: "order-events", fromBeginning: false }); await consumer.run({ eachMessage: async ({ partition, message }) => { const event = JSON.parse(message.value.toString()); switch (event.type) { case "order.placed": await emailService.sendConfirmation(event); break; case "order.shipped": await emailService.sendShippingNotification(event); break; } } }); }
Ne Zaman Kafka, Ne Zaman RabbitMQ?
RabbitMQ tercih edin: Karmaşık yönlendirme kuralları gerekiyorsa. Daha düşük hacim, daha kolay kurulum istiyorsanız. Task queue ihtiyacı varsa (iş dağıtımı).
Kafka tercih edin: Çok yüksek hacimli event streaming gerekiyorsa (saniyede milyonlarca mesaj). Mesaj geçmişine ihtiyaç varsa (replay, audit). Birden fazla bağımsız consumer aynı veriyi farklı amaçlarla işleyecekse.
Dead Letter Queue: Başarısız Mesajları Yönetmek
Consumer bir mesajı defalarca işlemeye çalışıp başarısız olursa ne olur? Mesajı tekrar tekrar denemeye devam etmek sistemi kilitler. Çözüm: Dead Letter Queue (DLQ).
Belirli sayıda başarısız denemeden sonra mesaj DLQ'ya taşınır. Orada incelenir, hata ayıklanır, gerekirse elle yeniden işlenir.
await channel.assertQueue("order_processing", { durable: true, arguments: { "x-dead-letter-exchange": "dlx", "x-message-ttl": 60000, "x-max-retries": 3 } });
Message queue'lar, dağıtık sistemlerin en güçlü yapı taşlarından biridir. Servisleri birbirinden ayırır, yük dengelemesi sağlar, hata toleransı ekler. Doğru kullanıldığında sistem hem daha sağlam hem daha ölçeklenebilir hale gelir.