Olay Güdümlü Mimari ve Kafka Nedir?

Olay Güdümlü Mimari (Event-Driven Architecture), Apache Kafka ve RabbitMQ nedir? Asenkron mesaj kuyrukları, Pub/Sub deseni ve gerçek zamanlı veri akışı.

Yozel

12 dakika

Olay Güdümlü Mimari ve Kafka Nedir?
İçindekiler (16)

Geleneksel yazılım sistemlerinde servisler arası iletişim çoğunlukla senkron (eşzamanlı) İstek/Yanıt (Request/Response - REST API veya gRPC) modeline dayanır. Bir kullanıcı e-ticaret sitesinde "Siparişi Tamamla" butonuna tıkladığında; Sipariş Servisi sırasıyla Ödeme Servisini, Stok Servisini, Fatura Servisini, Kargo Entegrasyon Servisini ve E-posta Bildirim Servisini HTTP üzerinden tek tek çağırır ve her birinin yanıt dönmesini bekler. Ancak bu senkron zincirde Fatura Servisi 5 saniye yavaşladığında veya E-posta sunucusu geçici olarak çöktüğünde, tüm sipariş akışı kilitlenir ve kullanıcı ekranında hata mesajı görür. İşte dağıtık sistemlerde bu kırılgan bağımlılığı (temporal coupling) ortadan kaldıran, saniyede milyonlarca veriyi sıfır kayıpla ve milisaniyelik gecikmeyle işleyen mimari yaklaşıma Olay Güdümlü Mimari (Event-Driven Architecture - EDA) adı verilir.

Bugün Uygulama ve Yazılım ekosisteminde küresel ölçekli finans platformlarından gerçek zamanlı lojistik takip sistemlerine kadar her modern altyapı, merkezinde Apache Kafka veya RabbitMQ gibi olay akışı ve mesajlaşma motorlarını barındırır. Bu kapsamlı mühendislik rehberinde; Event-Driven Architecture (EDA) prensiplerini, Apache Kafka'nın dağıtık günlük (commit log) mimarisini, Kafka ile RabbitMQ arasındaki derin teknik farkları, Event Sourcing (Olay Kaynaklama) yaklaşımını ve veri kaybını önleyen Transactional Outbox desenini tüm detaylarıyla inceliyoruz.

1. Olay Güdümlü Mimari (Event-Driven Architecture - EDA) Nedir?

Olay Güdümlü Mimari (EDA), yazılım bileşenlerinin veya mikroservislerin birbirlerini doğrudan çağırmak yerine, sistemde meydana gelen önemli durum değişikliklerini (Olay / Event) yayınlayarak ve dinleyerek tamamen asenkron (eşzamansız) biçimde haberleştiği bir yazılım tasarım paradigmasıdır.

EDA mimarisinde bir "Olay (Event)", sistemde geçmişte gerçekleşmiş ve değiştirilemez (immutable) bir gerçeği ifade eder: OrderCreated, PaymentCaptured, DriverLocationUpdated. Olayı üreten servise Producer (Üretici / Yayıncı), bu olayı alıp kendi iş kuralını çalıştıran servislere ise Consumer (Tüketici / Abone) denir. EDA'nın en büyük devrimi şudur: Producer, yayınladığı olayı kimin dinlediğini, kaç servisin tükettiğini veya o servislerin o anda ayakta olup olmadığını kesinlikle bilmez (Decoupling).

  • Zamansal Bağımsızlık (Temporal Decoupling): Sipariş Servisi OrderCreated olayını olay omurgasına (Event Broker) bıraktığı anda görevini tamamlar ve kullanıcıya 50 milisaniye içinde "Siparişiniz alındı" yanıtını döner. O sırada Fatura Servisi bakımda ve kapalı olsa bile sipariş kaybolmaz; Fatura Servisi 10 dakika sonra tekrar ayağa kalktığında kuyrukta bekleyen olayı okur ve faturayı keser.
  • Sıfır Kod Değişikliğiyle Genişletilebilirlik: Sisteme yeni bir "Yapay Zeka Dolandırıcılık Analizi (Fraud Detection)" servisi eklemek istediğinizde, Sipariş Servisinin koduna hiç dokunmazsınız; yeni servis sadece mevcut OrderCreated olay akışına abone olur. Bu prensip, modern Composable Commerce (Birleştirilebilir Ticaret) mimarilerinin de temel taşıdır.
  • Trafik Patlamalarını Sönümleme (Backpressure & Load Leveling): Black Friday gibi kampanya anlarında saniyede 50.000 sipariş geldiğinde, veritabanını doğrudan bu yükün altına sokmak yerine olaylar Kafka veya RabbitMQ üzerinde tamponlanır (buffering) ve arka uç servisler kendi kapasiteleri hızında (örneğin saniyede 5.000) bu olayları güvenle eritir.

2. Apache Kafka Nedir ve İç Mimarisi Nasıl Çalışır?

LinkedIn mühendisleri (Jay Kreps, Neha Narkhede ve Jun Rao) tarafından geliştirilen ve sonrasında Apache Yazılım Vakfı'na devredilen Apache Kafka, klasik bir mesaj kuyruğu değil; yüksek hacimli gerçek zamanlı veriler için tasarlanmış dağıtık bir olay akışı platformu (Distributed Event Streaming Platform) ve değiştirilemez kayıt günlüğüdür (Append-Only Commit Log).

Klasik mesaj kuyruklarında (örneğin RabbitMQ veya ActiveMQ) bir mesaj tüketici tarafından okunduğu anda kuyruktan silinir. Apache Kafka'da ise mesajlar okunduğunda silinmez! Kafka, gelen her olayı diske sıralı olarak (append-only) yazar ve yapılandırılan saklama süresi boyunca (7 gün, 30 gün veya sonsuza kadar) diskte tutar. Kafka'nın saniyede milyonlarca mesajı (gigabaytlarca veriyi) çok düşük donanım kaynağıyla işleyebilmesinin arkasında iki temel işletim sistemi optimizasyonu yatar:

  1. Sequential Disk I/O (Sıralı Disk Yazımı): Yaygın inanışın aksine, modern işletim sistemlerinde diske rastgele değil de tamamen sıralı (ardışık) veri yazmak, belleğe (RAM) rastgele erişmekten bile daha yüksek bant genişliği sunabilir. Kafka tüm verileri dosyanın sonuna eklediği (append-only) için disk kafası veya SSD blokları arama (seek) maliyeti yaşamaz.
  2. Zero-Copy (Sıfır Kopyalama) Tekniği: Geleneksel bir sunucu diskteki veriyi ağa gönderirken veriyi 4 kez kopyalar (Disk → OS Page Cache → Uygulama Belleği / JVM → Socket Buffer → NIC Buffer). Kafka ise Linux çekirdeğinin sendfile() sistem çağrısını kullanarak veriyi işletim sistemi önbelleğinden (Page Cache) uygulama katmanına hiç çıkarmadan doğrudan ağ kartına (NIC) aktarır.

2.1. Kafka'nın Temel Yapı Taşları: Topic, Partition, Offset ve Consumer Group

Apache Kafka ve RabbitMQ Mimari Karşılaştırması Topic Partition ve Exchange Yapısı
Apache Kafka'da olaylar Partition'lara bölünmüş sıralı loglarda (Offset) saklanırken, RabbitMQ'da Exchange üzerinden kuyruklara yönlendirilir.
  • Topic (Konu): Olayların kategorize edildiği mantıksal kanaldır (Örn: orders-events, payment-transactions, iot-sensor-telemetry).
  • Partition (Bölüm): Kafka'nın yatay ölçeklenebilirlik (horizontal scalability) sırrıdır. Bir Topic tek bir sunucuya sığmayacak kadar büyüdüğünde, kümedeki farklı sunuculara (Broker) dağılmış birden fazla Partition'a (Partition 0, Partition 1, Partition 2...) bölünür. Önemli Mimari Kural: Kafka, mesajların sırasını (ordering) tüm Topic genelinde değil, yalnızca tek bir Partition içinde garanti eder! Bu nedenle aynı müşterinin veya aynı siparişin olaylarının sırayla işlenmesi gerekiyorsa, mesaj gönderilirken Partition Key olarak orderId veya customerId verilir; Kafka bu anahtarın hash değerini alarak aynı siparişin tüm olaylarını her zaman aynı Partition'a yazar.
  • Offset (Konum İndeksi): Bir Partition içindeki her mesaja verilen, 0'dan başlayıp artan benzersiz sıra numarasıdır. Tüketiciler hangi mesajda kaldıklarını bu Offset numarasıyla takip eder. İhtiyaç halinde bir servis Offset'ini geçmiş bir tarihe sararak (Replay) tüm geçmiş olayları baştan okuyabilir!
  • Consumer Group (Tüketici Grubu): Aynı işi yapan mikroservis kopyalarının (instance) oluşturduğu gruptur. Örneğin 4 Partition'a sahip bir Topic'i dinleyen InvoiceServiceGroup içinde 4 adet pod (sunucu) varsa, her pod bir Partition'ı üstlenir ve yük 4 kat hızlı paralel işlenir. Aynı Topic'i aynı anda hem InvoiceServiceGroup hem de AnalyticsServiceGroup birbirinden tamamen bağımsız Offset'lerle okuyabilir.
  • KRaft (Kafka Raft): Eski Kafka sürümlerinde küme koordinasyonu için ayrı bir Apache ZooKeeper kümesi gerekirken, modern Kafka (3.x ve 4.x+) sürümlerinde ZooKeeper tamamen kaldırılmış ve KRaft konsensüs protokolü doğrudan Kafka çekirdeğine entegre edilmiştir.

3. Apache Kafka vs. RabbitMQ: Hangi Senaryoda Hangisi Seçilmeli?

Yazılım mimarlarının en sık karşılaştığı sorulardan biri "Kafka mı kullanmalıyız yoksa RabbitMQ mu?" sorusudur. Her iki teknoloji de mesajlaşma dünyasının devleri olsa da mimari felsefeleri taban tabana zıttır: RabbitMQ "Akıllı Broker / Basit Tüketici" (Smart Broker / Dumb Consumer) modelini benimserken; Apache Kafka "Basit Broker / Akıllı Tüketici" (Dumb Broker / Smart Consumer) modelini benimser.

RabbitMQ (AMQP protokolü), gelişmiş bir postane gibi çalışır: Mesaj önce Exchange bileşenine gelir; Exchange üzerindeki karmaşık yönlendirme kurallarına (Direct, Topic, Fanout, Headers) göre mesaj ilgili Queue (Kuyruk) yapılarına kopyalanır ve tüketiciye itilir (Push). Mesaj işlenip onaylandığında (ACK) kuyruktan silinir. Ayrıca mesaj önceliklendirme (Priority Queue) ve gecikmeli mesaj gönderimi gibi görev kuyruğu (Task Queue) senaryolarında RabbitMQ benzersizdir.

Karşılaştırma Kriteri Apache Kafka RabbitMQ
Temel Mimari Model Dağıtık Olay Günlüğü (Distributed Commit Log) Geleneksel Mesaj Aracısı (Message Broker - AMQP)
Mesaj Saklama Politikası Kalıcıdır (Okunsa bile günlerce/aylarca diskte kalır) Geçicidir (Tüketici ACK gönderdiği anda kuyruktan silinir)
Veri İşleme Kapasitesi (Throughput) Ultra Yüksek (Saniyede yüz binlerce / milyonlarca mesaj) Orta-Yüksek (Saniyede 10.000 - 50.000 mesaj)
Mesaj İletim Yöntemi Pull (Tüketici kendi hızında Broker'dan çeker) Push (Broker tüketicilere mesajı iter, prefetch ile sınırlar)
Yönlendirme (Routing) Yeteneği Temel (Topic ve Partition Key tabanlı) Çok Gelişmiş (Exchange tipleri, wildcard ve header filtreleme)
Geçmişi Tekrar Okuma (Event Replay) Destekler (Offset geri sarılarak geçmiş olaylar tekrar işlenir) Desteklemez (Silinen mesaj geri getirilemez)
İdeal Kullanım Alanı Event Sourcing, Gerçek Zamanlı Analitik, Log/IoT Akışı, CDC Arka Plan İş Kuyrukları (PDF/E-posta), Karmaşık Yönlendirme, RPC

4. İleri Seviye Dağıtık Desenler: Outbox Pattern, Idempotency ve Event Sourcing

Olay güdümlü bir mimari kurgularken sadece Kafka veya RabbitMQ kurmak yetmez; dağıtık sistemlerin doğasında bulunan ağ kopmaları ve kısmi çökmelere karşı veri tutarlılığını koruyacak 3 kritik tasarım desenini uygulamak zorunludur:

Transactional Outbox Pattern ve Event Sourcing Olay Kaynaklama Mimarisi
Transactional Outbox Pattern, veritabanı kaydı ile Kafka olay yayını arasındaki Dual-Write (Çift Yazma) tutarsızlığını tamamen ortadan kaldırır.

4.1. Dual-Write Problemi ve Transactional Outbox Pattern

Bir sipariş geldiğinde Sipariş Servisi önce kendi PostgreSQL veritabanına siparişi kaydeder (INSERT INTO orders...), hemen ardından kafkaProducer.send("OrderCreated") çağrısı yapar. Peki veritabanına kayıt başarılı olduktan tam 1 milisaniye sonra sunucunun elektriği kesilirse veya Kafka ağı koparsa ne olur? Sipariş veritabanında oluşmuştur ama Kafka'ya olay gitmediği için ne ödeme alınır ne de kargo çıkar! Buna Dual-Write (Çift Yazma) Problemi denir.

Çözüm Transactional Outbox Pattern'dir: Sipariş Servisi, orders tablosuna kayıt yaparken aynı yerel veritabanı işlemi (ACID Transaction) içinde outbox_events adındaki bir tabloya da gönderilecek olayı kaydeder. Böylece ya ikisi birden kaydedilir ya da ikisi birden iptal olur (Rollback). Ardından Debezium (Change Data Capture - CDC) gibi bir araç veya arka plan işçisi outbox_events tablosunu okuyarak olayı Kafka'ya garantili biçimde iletir.

4.2. En Az Bir Kez İletim (At-Least-Once Delivery) ve Idempotent Consumer

Ağ tabanlı hiçbir dağıtık sistemde mesajın hem kaybolmaması hem de asla mükerrer (çift) gitmemesi kolay değildir. Kafka ve RabbitMQ pratikte At-Least-Once (En Az Bir Kez) teslimat garantisiyle çalışır: Tüketici mesajı işleyip onay (ACK / Offset Commit) gönderirken ağ koparsa, Broker aynı mesajı tekrar gönderir. Eğer Ödeme Servisiniz aynı OrderCreated mesajını iki kez aldığında müşterinin kredi kartından iki kez para çekiyorsa büyük bir felaketle karşı karşıyasınız demektir!

Bunu önlemek için tüm tüketiciler Idempotent (Etkisiz / Tekrarlanabilir) tasarlanmalıdır: Her olayın içinde benzersiz bir eventId (UUID) bulunur. Tüketici servis mesajı işlerken veritabanındaki processed_events tablosuna bu eventId'yi kontrol ederek yazar; eğer aynı eventId daha önce işlenmişse ikinci mesajı sessizce yok sayar.

4.3. Event Sourcing (Olay Kaynaklama) ve Saga Pattern

Geleneksel sistemlerde veritabanında yalnızca nesnenin son durumu (current state) saklanır (Örn: bakiye = 1200 TL). Event Sourcing yaklaşımında ise veritabanında son durum değil, o duruma gelene kadar gerçekleşen tüm olaylar sırasıyla saklanır (HesapAçıldı(0) → ParaYatırıldı(+2000) → FaturaÖdendi(-800)). Tıpkı muhasebe yevmiye defteri veya blokzincir (blockchain) dağıtık defter teknolojisi gibi, geçmişteki hiçbir kayıt silinmez ve güncellenmez. Birden fazla mikroservisi kapsayan uzun iş akışlarında ise (Sipariş → Ödeme → Stok → Kargo) bir adımda hata çıktığında önceki adımları geri almak için Saga Pattern (Choreography veya Orchestration) kullanılarak telafi edici olaylar (Compensating Events - örn: RefundPaymentEvent) tetiklenir. Ayrıca legacy sistemlerdeki veri akışlarını analiz edip modernize ederken yapay zeka destekli tersine mühendislik araçları olay şemalarının (Avro / Protobuf Schema Registry) çıkarılmasında büyük hız kazandırır.

5. Dead Letter Queue (DLQ - Ölü Mektup Kuyruğu) ve Hata Yönetimi

Bir tüketici servis, bozuk formatlı bir mesaj (Poison Pill) veya geçici bir dış servis hatası nedeniyle bir olayı işleyemediğinde ne olmalıdır? Eğer hatayı yok sayıp devam ederse veri kaybolur; eğer aynı mesajı sonsuz döngüde tekrar denemeye kalkarsa arkadaki binlerce sağlam mesaj kilitlenir (Partition Blocking). Doğru mimaride, başarısız olan mesaj Exponential Backoff (Üstel Geri Çekilme - 1 sn, 5 sn, 30 sn) ile birkaç kez yeniden denenir (Retry Topic); yine başarısız olursa Dead Letter Queue (DLQ) adı verilen özel bir karantina kuyruğuna ayrılır ve geliştirici ekibe otomatik alarm gönderilir. Hata düzeltildikten sonra DLQ'daki mesajlar tekrar ana akışa sokulur.

6. Kafka Ekosistemi: Kafka Connect, Kafka Streams ve Consumer Lag İzleme

Apache Kafka'yı kurumsal üretim ortamlarında (production) sadece bir mesaj taşıyıcı olmaktan çıkarıp uçtan uca bir veri platformuna dönüştüren iki güçlü bileşeni ve kritik gözlemlenebilirlik (observability) metriği bulunur:

  • Kafka Connect (Sıfır Kodla Veri Entegrasyonu): Veritabanlarından (PostgreSQL, MySQL, MongoDB, Oracle) Kafka'ya veri çekmek (Source Connector) veya Kafka'daki olayları Elasticsearch, Snowflake, Amazon S3 ve BigQuery gibi sistemlere aktarmak (Sink Connector) için her seferinde özel kod yazmak gerekmez. Kafka Connect ve Debezium sayesinde veritabanının işlem günlüğü (Write-Ahead Log - WAL / Binlog) doğrudan dinlenerek tablolardaki her INSERT, UPDATE ve DELETE işlemi milisaniyeler içinde Kafka olayına dönüştürülür.
  • Kafka Streams ve Apache Flink (Akış İşleme - Stream Processing): Olayları veritabanına kaydetmeden önce havada (in-flight) analiz etmek, birleştirmek ve filtrelemek için kullanılır. Örneğin "Son 5 dakika içinde 3 farklı ülkeden kredi kartı denemesi yapılan hesapları anında bloke et" kuralı, kayan zaman pencereleri (Tumbling / Hopping / Sliding Windows) kullanılarak Kafka Streams veya Apache Flink üzerinde gerçek zamanlı olarak çalıştırılır.
  • Consumer Lag (Tüketici Geride Kalma Metriği): Olay güdümlü bir sistemin en hayati sağlık göstergesidir. Üreticinin Partition'a yazdığı son Offset (Log-End-Offset) ile Tüketicinin okuduğu son Offset (Current-Offset) arasındaki farka Consumer Lag denir. Eğer Consumer Lag sürekli büyüyorsa, tüketici servisleriniz gelen olay hızına yetişemiyor demektir; Kubernetes üzerinde KEDA (Kubernetes Event-driven Autoscaling) kullanılarak Consumer Lag arttığında pod sayısı otomatik olarak artırılır ve kuyruk eridiğinde tekrar düşürülür.

Sıkça Sorulan Sorular (SSS)

1. Apache Kafka bir veritabanı yerine kullanılabilir mi?

Kafka kalıcı disk depolama ve Log Compaction (anahtar bazlı son değeri saklama) özelliğine sahip olsa da ilişkisel bir veritabanı (PostgreSQL) veya arama motoru (Elasticsearch) yerine geçmez. Kafka'da karmaşık SQL sorguları (JOIN, WHERE, GROUP BY) çalıştıramazsınız; Kafka verinin hareket halindeki omurgasıdır (Data-in-Motion), sorgulama için verinin bir hedef veritabanına (Sink) aktarılması gerekir.

2. Mikroservisler arası iletişimde REST API tamamen bırakılıp sadece Kafka mı kullanılmalı?

Hayır. Kullanıcı arayüzünün anında cevaba ihtiyaç duyduğu sorgularda (örneğin ürün detayı görüntüleme veya kullanıcı girişi doğrulama) senkron REST / GraphQL / gRPC kullanılmalıdır. Arka planda gerçekleşen durum değişiklikleri, bildirimler ve servisler arası veri senkronizasyonu için ise asenkron Olay Güdümlü Mimari (Kafka / RabbitMQ) tercih edilmelidir.

3. Bir Kafka Topic'inde kaç adet Partition olmalı?

Partition sayısı paralel tüketici (Consumer) sayınızın üst sınırını belirler. Örneğin bir Consumer Group içinde en fazla 6 adet paralel sunucu (pod) çalıştırmayı planlıyorsanız, Topic'te en az 6 veya 12 Partition bulunmalıdır. Ancak gereksiz yere binlerce Partition açmak küme yönetim yükünü ve uçtan uca gecikmeyi artırır; küçük ve orta ölçekli servislerde 6 ile 24 arası Partition idealdir.

4. Schema Registry (Avro / Protobuf) neden kullanılır?

Kafka üzerinden düz JSON metinleri göndermek kolaydır ancak Üretici (Producer) ekip bir alanın adını değiştirdiğinde tüm Tüketici (Consumer) servisler çalışma zamanında çöker. Confluent Schema Registry ile Apache Avro veya Google Protobuf kullanıldığında, her olayın veri şeması versiyonlanır (Backward/Forward Compatibility) ve şemaya uymayan bozuk bir mesajın Kafka'ya yazılması daha kaynağında engellenir.

5. Küçük ölçekli bir projede veya No-Code entegrasyonlarında Kafka kurmak şart mıdır?

Hayır. Saniyede birkaç düzine işlemin yapıldığı projelerde veya No-Code / Low-Code B2B portal kurulumlarında kendi Kafka kümenizi yönetmek yerine bulut tabanlı yönetilen kuyruklar (AWS SQS/SNS, Google Pub/Sub, Upstash Kafka) veya hafif bir RabbitMQ / Redis Streams yapısı kullanmak operasyonel maliyeti büyük ölçüde düşürür.

Paylaş

Yozel

Codex haberleri en az iki bağımsız kaynakla doğrulanır; düzeltmeler yazının altında tarih ve saatle belirtilir. Yayın ilkelerimiz