Gönderen ile alan birbirini tanımak ya da aynı anda ayakta olmak zorunda değildir. Biri değişse de diğeri etkilenmez; sistem parçaları gevşek bağlı kalır.
İki servis birbirine doğrudan ve eşzamanlı bağlandığında, biri yavaşladığında ya da düştüğünde diğeri de onunla birlikte bekler. Mesaj kuyruğu, gönderen ile alanın arasına bir tampon koyar: gönderen mesajı bırakır ve işine devam eder, alan hazır olduğunda mesajı kendi hızında işler. Bu sayfada senkron ile asenkron iletişimin farkını, bir mesajın hangi rollerden geçtiğini, hangi teslimat garantileriyle taşındığını ve event-driven mimarinin bu işin neresinde durduğunu birlikte ve adım adım yan yana koyduk.
Senkron iletişimde gönderen, alandan yanıt gelene kadar bekler; iki servisin kaderi birbirine bağlanır. Asenkron iletişimde gönderen mesajı bir aracıya bırakıp işine devam eder, alan onu daha sonra işler. Mesaj kuyruğu (message queue) tam da bu aracıdır: gönderen ile alanı zaman ve hız açısından birbirinden ayırır (decoupling). Böylece alan yavaşladığında ya da geçici olarak düştüğünde mesajlar kaybolmaz, kuyrukta birikir ve sıra gelince işlenir.
# senkron — gönderen yanıtı bekler
servis A → servis B # B yavaşsa A da bekler
← yanıt
# asenkron — araya kuyruk girer
servis A → [ queue ] # A bırakır, devam eder
[ queue ] → servis B # B hazır olunca işler
Gönderen ile alan birbirini tanımak ya da aynı anda ayakta olmak zorunda değildir. Biri değişse de diğeri etkilenmez; sistem parçaları gevşek bağlı kalır.
Ani yük artışında mesajlar kuyrukta birikir, alan kendi hızında tüketir. Alan kısa süreli düşse bile mesajlar kaybolmaz.
Kullanıcıyı bekletmeden, e-posta gönderimi veya rapor üretimi gibi uzun işler arka plana atılır. Yanıt hızlanır, ağır iş sonra yapılır.
Asenkron mesajlaşma üç rol etrafında döner. Producer mesajı üreten ve gönderen taraftır; broker mesajı saklayan ve yönlendiren aracı sistemdir (RabbitMQ, Kafka gibi); consumer ise mesajı alıp işleyen taraftır. Broker'ın içinde mesajlar ya bir queue'da (sıralı, tek tüketiciye giden) ya da bir topic'te (yayınlanan, çok aboneye dağıtılan) tutulur. Bu rollerin net ayrılması, sistemin gevşek bağlı kalmasının temelidir.
Producer mesajı üretir ve publish eder
│
▼
Broker mesajı saklar ve yönlendirir
│ # queue ya da topic
▼
Consumer mesajı çeker, işler, ack'ler
# ack gelmezse broker mesajı yeniden teslim eder
Mesajı oluşturup broker'a gönderir. Alanı tanımaz; yalnızca hangi queue ya da topic'e yazacağını bilir. Mesaj gittikten sonra işine devam eder.
Mesajı kabul eder, dayanıklı biçimde saklar ve doğru tüketiciye yönlendirir. Sıralama, dağıtım ve teslimat garantilerinin uygulandığı yer burasıdır.
Mesajı broker'dan çeker, işler ve başarıyla bitince ack gönderir. Birden çok consumer aynı kuyruğu paylaşarak yükü bölüşebilir.
Bir mesajın kaç tüketiciye ve nasıl ulaşacağı bir desen seçimidir. Point-to-point queue'da her mesaj tek bir tüketici tarafından işlenir; iş bölüşülür. Publish/subscribe'da (pub/sub) bir topic'e yayınlanan mesaj tüm abonelere kopyalanır; aynı olay birçok yerde tepki üretir. Fan-out, tek mesajın birçok kuyruğa dağıtıldığı bu yayılmanın adıdır. Doğru desen, "bu mesajı biri mi yapsın, herkes mi duysun" sorusunun yanıtına bağlıdır.
| Desen | Kim alır | Tüketici sayısı | Uygun olduğu yer |
|---|---|---|---|
Point-to-point |
Tek tüketici | Mesaj başına bir kez | İş kuyruğu, görev dağıtımı |
Pub/Sub |
Tüm aboneler | Abone başına bir kopya | Olay yayını, çoklu tepki |
Fan-out |
Birçok kuyruk | Kuyruk başına dağıtım | Tek olayı paralel işlemek |
Consumer group |
Grup içinde tek üye | Bölüm (partition) başına | Ölçeklenir sıralı tüketim |
Mesaj tek bir tüketiciye gider; birden çok tüketici varsa iş aralarında bölüşülür. Aynı işi iki kez yaptırmamak için idealdir.
Bir olay birçok bağımsız aboneyi tetikler. "Sipariş verildi" olayına hem fatura hem stok hem bildirim servisi ayrı ayrı tepki verir.
Kafka'da bir grup içindeki tüketiciler partition'ları paylaşır; hem yük dağılır hem partition içinde sıralama korunur.
Bir mesajın "kaç kez teslim edileceği" sistemin en kritik tasarım kararıdır. At-most-once en fazla bir kez teslim eder; kayıp olabilir ama tekrar olmaz. At-least-once en az bir kez teslim eder; kayıp olmaz ama aynı mesaj birden çok gelebilir. Exactly-once arada idealdir ama gerçekte yalnızca idempotent tüketici ile yaklaşılır. Anahtar mekanizma ack'tir: tüketici işi bitirince onay gönderir, gelmeyen mesaj yeniden teslim edilir.
broker → mesaj teslim eder
consumer işi yapar
→ ack # başarı: broker mesajı siler
broker → mesaj teslim eder
consumer çöker, ack yok # broker yeniden teslim eder
→ mesaj 2. kez gelir # bu yüzden idempotency gerekir
message id) kaydedip tekrarı atlamak en yaygın yöntemdir.
Event-driven mimaride servisler birbirini doğrudan çağırmaz; olan biteni bildiren olaylar (event) yayınlar ve ilgilenen servisler bunlara tepki verir. Bir event "olmuş bir şeyi" anlatır (OrderPlaced); bir command ise "yapılması istenen bir şeyi" söyler (PlaceOrder). Bu fark koordinasyon biçimini belirler: orchestration'da merkezî bir akış yönetir, choreography'de her servis kendi tepkisini olaylardan çıkarır.
OrderPlaced # olmuş bir şey yayınlanır
├─→ Fatura servisi fatura oluştur
├─→ Stok servisi stok düş
└─→ Bildirim servisi e-posta gönder
# producer aboneleri tanımaz; yenisi eklenince
# mevcut kod hiç değişmeden tepki verir.
Event olmuş bir gerçeği bildirir ve birçok aboneye açıktır; command belirli bir alıcıya "şunu yap" der. Event geçmiş zaman, command emir kipidir.
Choreography'de her servis olaylara bakıp kendi adımını atar; orchestration'da merkezî bir koordinatör akışı yönetir. İlki gevşek bağlı, ikincisi izlenebilir kalır.
Son durumu değil, ona götüren tüm olayları saklamak. Durum, olayların yeniden oynatılmasıyla üretilir; tam denetim izi ve geçmişe dönük analiz sağlar.
Araçlar iki temel yaklaşım etrafında toplanır. RabbitMQ gibi geleneksel broker'lar mesajı tüketildikten sonra siler ve zengin yönlendirme sunar; Kafka gibi sistemler ise mesajları kalıcı bir log olarak tutar, tüketiciler aynı veriyi tekrar tekrar okuyabilir. Bunların yanında SQS gibi yönetilen bulut kuyrukları ve Redis Streams gibi hafif çözümler vardır. Doğru seçim, yönlendirme ihtiyacınıza, kalıcılık beklentinize ve işletme yükünüze bağlıdır.
Zengin yönlendirme (exchange, routing key) ve esnek teslimat. Mesaj tüketilince düşer; iş kuyruğu ve karmaşık dağıtım senaryolarında güçlüdür.
Mesajları partition'lı, kalıcı bir log olarak tutar. Yüksek hacimli olay akışı, event sourcing ve birden çok tüketicinin aynı veriyi okuduğu durumlar için biçilmiş kaftandır.
Sunucu yönetmeden ölçeklenen bulut kuyruğu. Standart (at-least-once) ve FIFO seçenekleriyle, işletme yükünü en aza indiren pratik bir başlangıçtır.
Zaten Redis kullanıyorsanız, consumer group destekli hafif bir akış yapısı. Düşük gecikme ve sade kurulum ister, devasa hacim hedeflemez. Ayrıntı için Önbellekleme & Redis başlığına bakabilirsiniz.
Redis üzerine kurulu, uygulama içi arka plan iş kuyruğu. E-posta, rapor ve görüntü işleme gibi gecikmeli görevleri ana isteğin dışına taşır.
Sade ve çok hızlı bir mesajlaşma sistemi; JetStream ile kalıcılık ekler. Servisler arası hafif, gecikmeye duyarlı iletişimde tercih edilir.
Asenkron mesajlaşma esneklik kazandırırken yeni başarısızlık biçimleri de getirir. Aynı mesaj birden çok kez gelebilir (duplicate); sıra garanti edilmeyebilir (ordering); işlenemeyen mesajlar sistemi tıkayabilir (poison message); tüketici üreticiye yetişemeyince kuyruk şişer (backpressure). Bunların her birinin bilinen bir savunması vardır ve dead letter queue, çoğunun ortak emniyet supabıdır.
At-least-once teslimatta aynı mesaj birden çok gelebilir. Tüketiciyi idempotent yapmak ve işlenen mesaj kimliklerini saklamak bunu zararsız kılar.
Çoğu sistem global sıra garanti etmez; paralel tüketicilerde mesajlar karışabilir. Sıra önemliyse aynı anahtar aynı partition'a yönlendirilir.
Belirli denemeden sonra işlenemeyen mesaj ayrı bir kuyruğa (DLQ) taşınır. Ana akış tıkanmaz, sorunlu mesaj sonra incelenir.
Hep hata veren bir mesaj sonsuz döngüde yeniden teslim edilir ve tüketiciyi kilitler. Deneme sayısını sınırlayıp DLQ'ya atmak çözer.
Tüketici üreticinin hızına yetişemeyince kuyruk şişer ve bellek dolar. Tüketiciyi ölçeklemek, hız sınırlamak ve kuyruk derinliğini izlemek gerekir.
Uçtan uca "tam bir kez" çoğu zaman bir efsanedir. Gerçekçi hedef at-least-once teslimat ile idempotent tüketicidir; bu pratikte aynı sonucu verir.
Queue point-to-point çalışır: her mesaj tek bir tüketici tarafından işlenir, iş bölüşülür. Topic pub/sub çalışır: yayınlanan mesaj tüm abonelere kopyalanır. Soru basittir: bu mesajı biri mi yapsın, yoksa herkes mi duysun.
Message taşınan herhangi bir veri paketinin genel adıdır. Event ise olmuş bir şeyi bildiren özel bir mesaj türüdür ve genellikle yayınlanır. Her event bir message'dır ama her message bir event değildir; bir command de bir message'dır.
Kafka mesajları kalıcı bir log olarak tutar; tüketiciler aynı veriyi tekrar okuyabilir, yüksek hacme uygundur. RabbitMQ klasik bir broker'dır; mesaj tüketilince düşer, zengin yönlendirme sunar. Olay akışı için Kafka, esnek iş kuyruğu için RabbitMQ öne çıkar.
At-least-once mesajı kaybetmez ama tekrar teslim edebilir. Exactly-once her mesajı tam bir kez işler ama uçtan uca garantisi pahalı ve sınırlıdır. Pratikte at-least-once + idempotent tüketici, exactly-once'ın güvenilir karşılığıdır.
Choreography'de servisler olaylara bakıp kendi adımını bağımsızca atar; gevşek bağlıdır ama akışı izlemek zorlaşır. Orchestration'da merkezî bir koordinatör adımları sırayla yönetir; izlenebilir ama merkeze bağımlıdır.
RabbitMQ, Kafka ve Amazon SQS'in resmi dokümantasyonu, olay biçimini standartlaştıran CloudEvents ve Redis Streams gibi tarafsız teknik referanslar.
Doğru kurulmuş bir mesaj kuyruğu, sistemin parçalarını birbirinden ayırır ve yükü dengeler; yanlış kurulmuş bir kuyruk ise sessiz kayıplar ve takılmalar üretir. Nereye kuyruk koyacağınıza, hangi teslimat garantisini seçeceğinize ve olayları nasıl kurgulayacağınıza birlikte bakalım.