Published on

Apache Kafka Notları

Authors

Apache Kafka, yüksek performanslı ve ölçeklenebilir bir veri akışı (streaming) platformudur. İlk olarak LinkedIn tarafından geliştirilmiş olan bu açık kaynaklı platform, büyük miktarda veri akışını güvenilir bir şekilde işlemek ve iletmek için tasarlanmıştır.

Kafka'nın temel özellikleri şunlardır:

  1. Dayanıklılık: Kafka, veriyi diskte saklayarak dayanıklılığı sağlar. Bu, veri kaybını önlemek ve sistem çökmesi durumunda veriyi korumak anlamına gelir.

  2. Yüksek Performans: Kafka, binlerce mesajı saniyede işleyebilecek hızda çalışabilir. Bu özellik, büyük ölçekli veri akışlarını yönetmek için uygundur.

  3. Dağıtık Yapı: Kafka, birçok sunucu üzerinde çalışabilen dağıtık bir mimariye sahiptir. Bu sayede ölçeklenebilirlik ve yüksek erişilebilirlik sağlanır.

  4. İş Parçacığı Desteği: Kafka, çoklu tüketici ve üretici iş parçacıklarını destekleyerek, aynı anda birçok görevi yerine getirebilir.

  5. Esnek Veri Modeli: Kafka, farklı formatlardaki verilerle çalışabilir. JSON, Avro gibi yaygın veri formatlarını destekler.

Zengin API Desteği: Kafka, çeşitli programlama dilleri için zengin bir API setine sahiptir. Bu da geliştiricilerin kolaylıkla entegrasyon yapmalarına olanak tanır.

Apache Kafka, özellikle büyük veri akışları, gerçek zamanlı analiz ve olay işleme uygulamalarında tercih edilen bir çözümdür. Veri entegrasyonu, günlükleme, uygulama izleme gibi birçok senaryoda kullanılabilir.

Kafka'nın aşağıdaki bileşenleri bulunmaktadır:

Broker

En basit haliyle broker, Kafka instancelarıdır. Kafka'nın çalışan parçacıklarına broker denir ve içinde topic'leri barındırır.

Cluster

Birden çok broker'ın bir araya gelerek oluşturduğu yapıya cluster denir.

Zookeeper

Kafka'da mantıksal işlemleri yapan ve kararları veren yapıya Zookeeper denir. Bir nevi Kafka'nın beyni olarak düşünülebilir. Mesajın kim tarafından nereye yazılacağı, kim tarafından nasıl okunacağı, master partition kim olacak gibi birçok mantıksal kararı veren ve Kafka yapılarını yöneten bir uygulamadır.

Message

Kafka'da taşınan veriye mesaj deniliyor. Mesaj bir çok formatta olabilir. Bu formatları detaylıca açıklayacağımız aşağıda, mesajların body'si ve header'ı olabilir. Genelde body'de datayı, header'da ise configuration ile ilgili detayları saklarız. Message'in bir de key'i vardır ve bu da bizim partition'lara yazarken kullanacağımız bir Kafka özelliğidir.

Topic

Aslında Kafka'daki ana yapı topic'tir. Mesajlar buraya yazılır ve buradan okunur. Topic içerisinde partition dediğimiz bir alan vardır, burada mesajlar saklanır. Bir topic içerisinde birden çok partition olabilir. Bu detayları ileride göreceğiz.

Partition

Topic'lerin içerisinde yer alan queue yapılarına benzeyen yapılarıdır. Bir partition içerisinde ilgili mesaj ve bu mesajın offset bilgisi tutulur. Bu bilgiler eşliğinde topic içerisinde mesajın yazılması ve okunması partitionlar üzerinde gerçekleştirilir. Bir topic içerisinde birden fazla partition bulunabilir, bunun sayısının ne kadar olacağı birden çok parametre bağlıdır. Detaylar best practice'lerde.

Offset

Offset, partition içerisinde bulunan Kafka tarafından gelen her mesajın ilgili partition'a yazma sırasına göre arttırılan bir auto-increment değeridir. Kafka bir mesajın yazılması sırasında Offset'i arttırır, okunurken de en son okunan verinin ne olduğu offset bilgisiyle tutulur. __consumer_offsets topic'inde bu değer tutulur.

Producer

Kafka'da mesajı yazan kişiye "producer" denir. Nasıl yazıyor derseniz, belirli bir veriyi bir "topic"e gönderir. Zookeeper, bu mesajın topic içerisinde hangi partition'a yazılacağına karar verir ve mesajı yazar. Mesaj yazıldıktan sonra ilgili producer'a "ack" (onay) gönderilir, yani "Ben mesajını yazdım" denir.

Zookeeper, bu mesajın hangi partition'a yazılacağını nasıl karar veriyor? Eğer messageId daha önce bir partition'da varsa, o partition'a yazılır yoksa, varsayılan olarak "round robin" algoritması kullanılır. Bu şekilde bir partition içerisine mesaj yazılmış olur. "Ack" (onay) modları var, bunları açıklayacağız.

Producer Delivery Semantics

1. At-most-once (En Fazla Bir Kez)

  • Bu semantikte, üretici (producer) bir mesajı yalnızca bir kez gönderir. Gönderilen mesajın başarılı bir şekilde iletildiğine dair bir onay alınmaz.
  • Bu durumda, ağ hataları veya broker'da bir hata olması gibi durumlar mesajın kaybolmasına neden olabilir.
  • Bu yöntemde, mesaj en fazla bir kez iletildiği için kaybolma riski olabilir, ancak performans genellikle daha iyidir.

2. At-least-once (En Az Bir Kez)

  • Bu semantikte, üretici bir mesajı en az bir kez gönderir.
  • Gönderilen mesajın başarıyla iletildiği bir onay alındığında, üretici mesajın başarıyla teslim edildiğini kabul eder.
  • Ancak, bu durumda aynı mesajın birden fazla kez işlenme riski vardır.
  • Örneğin, bir hata nedeniyle onay alınamazsa, aynı mesaj tekrar gönderilebilir. Bu durumu ele almak için ek kontrol mekanizmalarına ihtiyaç duyulabilir.

3. Exactly-once (Tam Olarak Bir Kez)

  • Bu semantik, hem üretici hem de tüketici tarafında tam olarak bir kez teslimat sağlamayı amaçlar.
  • Bu genellikle daha karmaşık bir işleme süreci gerektirir ve Kafka'da "idempotent producer" ve "transactional producer" kullanımını içerebilir.
  • İdempotent producer, aynı mesajın birden fazla kez gönderilmesini önler, transactional producer ise işlemleri birbirine bağlayarak tam olarak bir kez teslimat sağlar.
  • Tam olarak bir kez teslimat elde etmek, at-least-once semantiğinin sağlanmasına göre daha zor ve maliyetlidir.

Tam olarak bir kez teslimat elde etmek, at-least-once semantiğinin sağlanmasına göre daha zor ve maliyetlidir. Hangi teslimat semantiğinin seçileceği, uygulamanın gereksinimlerine ve kullanım durumlarına bağlıdır. At-most-once, düşük maliyet ve yüksek performans sağlar, ancak kaybolan mesajlara tolerans gösterilebilir durumdaysa kullanılabilir. At-least-once, veri kaybına karşı daha dayanıklıdır ancak mesajların çift gönderilme olasılığı vardır. Exactly-once, en güvenli seçenek olmakla birlikte, performans açısından daha pahalı olabilir.

Spring Boot'ta Kafka kullanırken, acks (acknowledgment) ayarı sayesinde üretici (producer) tarafındaki teslimat semantiği belirlenir. Varsayılan olarak, Spring Kafka, acks ayarını "1" olarak ayarlar.

Bu durum, "At-least-once" teslimat semantiğini ifade eder. Yani, üretici bir mesajı gönderdikten sonra en az bir broker'ın bu mesajı aldığını onay alana kadar işleme devam ettiğini belirtir. Bu ayar, mesajın kaybolma olasılığını minimize eder, ancak aynı mesajın birden fazla kez işlenme olasılığını içerir.

Consumer

Kafka'da mesajları okuyan uygulamalara "consumer" denir. Consumer'lar mesajları topic'lerin partition'larında okur, okuduktan sonra "ack" (onay) bilgisini iletir. Yani, "Ben bu partition'daki bu mesajı okudum, offset'imi arttırabilirsin" der. Zookeeper da arttırır ve bu şekilde ilerleyerek tüm mesajları tüketmiş olur.

Consumer Delivery Semantics

1. At-Most-Once (En Fazla Bir Kere)

  • Bu semantikte, bir tüketici mesajı işlediğinde, Kafka broker'ına geri dönüp başarılı bir şekilde işlendiğini belirtmez.
  • Mesaj bir kere işlendikten sonra kaybolabilir veya tekrar işlenebilir.
  • Bu durum, veri kaybına tolerans gösterilebilecek durumlar için uygundur.

2. At-Least-Once (En Az Bir Kere)

  • Bu semantikte, bir tüketici bir mesajı işlediğinde, işlemin başarılı olduğunu Kafka broker'ına bildirir.
  • Ancak, eğer işlenen mesajın onayı alınmazsa (örneğin, bir hata nedeniyle), aynı mesajın tekrar işlenme riski vardır.
  • Bu durum, veri kaybına daha az tolerans gösterilebilecek ve biraz daha güvenilir bir tüketim gerektiren durumlar için uygundur.

3. Exactly-Once (Tam Olarak Bir Kere)

  • Bu semantik, bir mesajın yalnızca bir kere işlendiğinden ve işlendikten sonra tekrar işlenmediğinden emin olmayı amaçlar.
  • Bu genellikle daha karmaşık bir yapı gerektirir ve Kafka, "idempotent consumer" ve "transactional consumer" kullanımını içerebilir.
  • Tam olarak bir kez teslimat elde etmek, at-least-once semantiğinin sağlanmasına göre daha zor ve maliyetlidir.

Genellikle, teslimat semantiği seçimi uygulamanın gereksinimlerine ve kullanım durumlarına bağlıdır. At-most-once, düşük maliyet ve yüksek performans sağlar, ancak kaybolan mesajlara tolerans gösterilebilir durumdaysa kullanılabilir. At-least-once, veri kaybına karşı daha dayanıklıdır ancak mesajların çift gönderilme olasılığı vardır. Exactly-once, en güvenli seçenek olmakla birlikte, performans açısından daha pahalı olabilir.

Spring Boot kullanırken, Kafka tüketici (consumer) tarafındaki varsayılan ayarlar genellikle "At-Most-Once" teslimat semantiği ile ilişkilidir. Bu durum, tüketicinin mesajı alıp işledikten sonra hemen Kafka broker'ına başarı bilgisi (acknowledgment) göndermediği anlamına gelir. Dolayısıyla, bir hata durumunda mesajın tekrar işlenme riski bulunabilir.

Bu varsayılan davranış, enable.auto.commit özelliği tarafından kontrol edilir. Eğer bu özellik true olarak ayarlanırsa, tüketici mesajları otomatik olarak işledikten sonra broker'a başarı bilgisi gönderir ve bu durum "At-Least-Once" semantiği ile uyumludur.

Aşağıda, application.properties veya application.yml dosyasında enable.auto.commit özelliğini belirleyerek tüketici tarafındaki teslimat semantiğini değiştirmenin bir örneği:

spring.kafka.consumer.enable-auto-commit=false

Yukarıdaki ayar, otomatik onaylamayı devre dışı bırakır ve uygulamanın mesajları başarıyla işledikten sonra onaylamayı (commit) kendisi gerçekleştirmesine olanak tanır. Bu durum, mesajların yalnızca onaylandığında Kafka broker'ında işlemin gerçekleştiği kabul edilir, bu da "En Az Bir Kere" teslimat semantiğini sağlar.

Consumer group

Yoğun yük altında kullandığımız uygulamalarda, Kafka'da topiclere mesaj yazma hızı, okuma hızından genellikle daha hızlıdır. Bu durumun nedeni, consumer'ın mesajı işlerken yapılan işlerin süresinin uzun olmasıdır. Bu süreyi hızlandırmak için çeşitli yöntemlerimiz var. Kafka'da, topic içerisindeki partition sayısını arttırırsak, her partition'i bir consumer dinlerse paralel olarak bu topic'teki mesajları tüketmiş oluruz. İşte bu consumer'ların oluşturduğu gruba consumer-group deniliyor. Genellikle consumer group içinde bulunan üye sayısının partition sayısı kadar olması tercih edilir, çünkü partition sayısından fazla consumer group eklersek fazladan eklediğimiz üye, başka bir üye pasif duruma geçene kadar hiçbir şey yapmaz. Bir partition i sadece bir consumer group dinleyebilir. Bir consumer gruop ise birden cok partition i dinleyeblir,

Auto Offset Reset

Auto Offset Reset, Apache Kafka'nın consumer grupları için önemli bir konfigürasyon parametresidir. Bu ayar, bir tüketici grubunun başlangıç pozisyonunu belirler. Bu ayar, tüketici grubu bir topiğe abone olduğunda ve daha önce bir konuda işlem yapmamışsa ne yapılacağını belirler.

Bu ayarın değerleri genellikle şunlardır:

earliest: Bu değer, consumer grubunun konu üzerinde en erken (ilk) mesajdan başlamasını sağlar. Yani, consumer grubu daha önce konu üzerinde işlem yapmamışsa en eski mesajdan itibaren işlem yapmaya başlar.

latest: Bu değer, consumer grubunun konu üzerinde en son mesajdan başlamasını sağlar. Yani, consumer grubu daha önce konu üzerinde işlem yapmamışsa en son mesajdan itibaren işlem yapmaya başlar.

Bu ayarı genellikle consumer grubu ilk kez başlatıldığında veya consumer grubu bir süre inaktif kaldığında kullanmak önemlidir. Bu, consumer grubunun konu üzerindeki mevcut duruma uygun bir başlangıç pozisyonunu belirlemesine yardımcı olur.

Java Spring Boot ile Kafka kullanırken, auto.offset.reset ayarını application.properties veya application.yml dosyasında aşağıdaki gibi ayarlayabilirsiniz:

spring.kafka.consumer.auto-offset-reset=earliest

veya

spring.kafka.consumer.auto-offset-reset=latest

Replication

Kafka'da verinin kaybolmaması için partition'ların yedekleri (replication) kullanılır.

  • Her partition, birden fazla broker arasında çoğaltılır.
  • Her partition bir lider (leader) ve bir veya daha fazla takipçi (follower) ile ilişkilidir. Lider, yazma işlemlerini yönetir ve takipçilere bu veriyi çoğaltır.
  • Lider, veri eklediğinde belirli zaman aralıklarında bu veriyi takipçilere çoğaltır (replicate).
  • Liderle takipçiler arasındaki veri bütünlüğünü sağlamak için her takipçi, liderle senkronize olmuş durumda bulunmalıdır (In-Sync Replicas).
  • Eğer liderle iletişim kurulamazsa (örneğin bir broker'a erişilemezse), Kafka, ISR (In-Sync Replicas) içinde bulunan bir takipçiyi lider olarak seçerek devamlılığı sağlar. Bu, bir liderin düşmesi durumunda sistemin çalışmaya devam etmesini sağlar.

Clean Up Policy And Compaction

  1. Clean Up Policy
  • Kafka'da her bir topic için bir "Clean Up Policy" belirlenebilir.
  • Temizleme politikası, mesajların topic içinde ne zaman ve nasıl temizleneceğini belirler.

İki temel temizleme politikası bulunur:

  • Delete : Bu politika, belirli bir saklama süresini aşan mesajları temizler. Örneğin, bir günü aşan mesajları sil.
  • Compact : Bu politika, topic içindeki "key-value" çiftlerini temizler ve sadece en son değeri saklar. Bu sayede, aynı key'e sahip farklı değerleri temizler, veri boyutunu azaltır.
  1. Compaction
  • Kafka'da bir topic içindeki veriyi sıkıştırmak için kullanılan bir mekanizmadır.
  • Bu mekanizma, topic içindeki mesajları unique key'ler üzerinden sıkıştırarak gereksiz veri birikimini önler.
  • Compaction, özellikle key-value veri modellerinde faydalıdır. Aynı anahtara sahip farklı değerler arasından sadece en son değeri tutarak veri boyutunu optimize eder.
  • "Log Compaction" olarak da adlandırılır.

Örnek bir kullanım senaryosu: Bir kullanıcının profil bilgilerini içeren bir topic düşünelim. Eğer bir kullanıcının profil bilgilerinde güncelleme yapıldığında sadece en son güncellenmiş değeri saklamak istiyorsak, bu durumda compaction mekanizması kullanılabilir.

Clean Up Policy ve Compaction, Kafka'nın veri yönetimi ve depolama stratejilerini belirlemede esneklik sağlar. Bu politikalar, veri saklama süreleri, boyutları ve gereksiz veri birikimini kontrol etmek için kullanılır.

Retention

Retention politikası aslında clean up policy ile ilgilidir, Kafka'da belirli bir topic içindeki mesajların ne kadar süreyle tutulacağını belirleyen bir ayar veya stratejidir. Bu politika, veri saklama süresini kontrol etmek, depolama alanını yönetmek ve gereksiz veri birikimini önlemek amacıyla kullanılır.

İki ana türde Retention politikası bulunur:

  1. Time-based Retention (Zamana Bağlı Saklama):
  • Bu politika, mesajların belirli bir zaman süresi boyunca saklanmasını sağlar.
  • Örneğin, bir topic için "1 gün" süresince saklama politikası belirlenebilir. Bu durumda, bir günü aşan mesajlar otomatik olarak silinir.
  1. Size-based Retention (Boyuta Bağlı Saklama):
  • Bu politika, bir topic'in belirli bir boyutta kalmasını sağlar.
  • Örneğin, bir topic için "100 MB" boyutunda bir saklama politikası belirlenebilir. Bu durumda, topic içindeki mesajlar toplamda 100 MB'yi aştığında eski mesajlar silinir.

Retention politikası, özellikle zamanla büyüyen ve sürekli olarak veri üretilen sistemlerde, veri birikimini kontrol etmek ve gereksiz veri saklamaktan kaçınmak için bu politikalar kullanılır.

Bir topic'e özel olarak retention politikası belirlenebileceği gibi, broker veya cluster seviyesinde genel bir default retention politikası da tanımlanabilir. Bu sayede farklı topic'lerde farklı saklama politikaları uygulanabilir.

Lag

En basit haliyle lag, bir partition'da işlenmemiş mesaj miktarını ifade eder. Lag sayısı beklenmedik bir şekilde artıyorsa, bu genellikle consumer tarafında bir sorunun işareti olabilir. Örneğin, consumer projesi aşırı yük altında olabilir, mesaj işleme sırasında hatalarla karşılaşmış olabilir. Bu durumlar, genellikle monitoring uygulamaları aracılığıyla takip edilir ve belirli konfigürasyonlara dayalı olarak alert mekanizmaları kurularak hızlı bir şekilde müdahale edilmesi sağlanır.

Sonuç

Yazıyı okuduğunuz için teşekkür ederim, umarım faydalı olmuştur. Bu yazıyı aslında Apache Kafka ile ilgili kendime çıkardığım notları tek bir dökümanda bulmak için yazdım, bunu yaparken ChatGpt'den sık sık destek aldım. Bu bahsettiğim konulara ek dead letter queue, retry mekanizmaları gibi başlıkları da içeren örnek bir proje yapacagım, github reposunu aşağıya ekleyeceğim, başka notlarda görüşmek ūzere.

Herkese iyi çalışmalar. ✌🏼