Kafka Streams'e Giriş

İçindekiler
- Kafka Nedir?
- Kafka Streams
- Kafka Streams Güçlü Ve Zayıf Yönleri
- Kafka Streams Kullanım Senaryoları
- Sonuç
- Kaynakça
Kafka Nedir?
Apache Kafka, büyük veri akışlarını yüksek hızda işlemek, aktarmak ve depolamak için kullanılan dağıtık veri akışı platformudur. Başlangıçta LinkedIn tarafından geliştirilen Kafka, Apache Software Foundation’a devredilerek açık kaynak projesine dönüştürülmüştür. Kafka, veri akışı platformlarının ve mesajlaşma sistemlerinin bir adım ötesine geçerek hem veri akışı hem de dağıtık depolama sistemi olarak işlev görür. Bu çift yönlü yapı, sistemlerin büyük miktarda veriyi güvenilir bir şekilde ele almasını sağlar.
Kafka, mesajlaşma sistemlerinin (message broker) daha gelişmiş ve çok yönlü bir versiyonu olarak düşünülebilir. Sadece mesaj iletmekle sınırlı kalmaz; aynı zamanda büyük veri kümelerini verimli bir şekilde işleyip saklayarak, modern veri odaklı sistemlerin bel kemiğini oluşturur.

Kafka’nın Temel Bileşenleri
Kafka’nın çalışma yapısını daha iyi anlamak için temel bileşenlerini incelemek önemlidir. Bu bileşenler, verinin üretiminden tüketimine kadar her aşamada kritik bir rol üstlenir.
1. Topic (Konu)
Kafka’da veriler, topic adı verilen kategorilere gönderilir ve organize edilir. Her bir topic, belirli bir veri türünü ya da kategorisini temsil eder. Örneğin e-ticaret platformlarında sipariş verileri ve müşteri geri bildirimleri iki farklı topic olabilir. Kafka'nın veri akışını bu şekilde düzenlemesi, verinin kolayca yönetilmesini ve ilgili veriye hızlı erişim sağlanmasını mümkün kılar.
2. Partition (Bölüm)
Topic'ler kendi içinde partition adı verilen parçalara bölünür. Bu bölünme, verinin daha küçük parçalara ayrılmasını ve her bir parçanın sıralı kaydının tutulmasını sağlar. Partition yapısı, büyük veri akışlarının paralel işlenmesine olanak tanıyarak Kafka’nın yüksek performanslı ve ölçeklenebilir olmasını sağlar. Bu sayede, aynı topic içindeki veriler farklı sunucularda dağıtık olarak saklanabilir, bu da sistemin hızını ve güvenilirliğini artırır.
3. Producer (Üretici)
Veriyi Kafka’ya gönderen bileşene producer denir. Producer, belirli bir topic'e veri yazar ve bu veri Kafka aracılığıyla tüketicilere iletilir. Kafka'nın en güçlü özelliklerinden biri, producer'ların veriyi hızlı ve güvenilir bir şekilde Kafka’ya iletebilmesidir. Producer'lar, genellikle mikroservisler (mikro hizmet), uygulamalar veya veri kaynakları olabilir.
4. Consumer (Tüketici)
Kafka’dan veri okuyan bileşenler consumer olarak adlandırılır. Consumer’lar, tanımlandıkları topic'ten gelen veriyi alır ve işler. Kafka, aynı veriyi birden fazla consumer grubuna iletebilir ve her grup bu veriyi farklı şekillerde işleyebilir. Bu esneklik, farklı iş ihtiyaçlarına göre özelleştirilebilen veri işleme senaryolarını destekler.
5. Broker (Aracı)
Kafka'nın arka planında çalışan sunuculara broker denir. Bir Kafka cluster'ı (Kafka kümesi), genellikle birden fazla broker'dan oluşur. Broker’lar, veriyi saklar, producer ile consumer'lar arasındaki veri akışını yönetir ve yüksek erişilebilirlik sağlar. Verinin dağıtık olarak saklanması, olası bir sunucu hatasında bile verinin kaybolmamasını ve Kafka’nın sürekli çalışmasını garanti eder.
Şekil 2’de de görüldüğü üzere Kafka’nın temel bileşenleri, büyük veri setlerinin güvenilir ve ölçeklenebilir bir şekilde işlenmesini sağlayarak modern veri altyapılarının merkezinde yer alıyor. Kafka’nın gücü, sadece büyük miktarda veriyi taşımaktan öte, bu veriyi düzenli ve tutarlı bir şekilde işlemesinden gelir.

Kafka, farklı işlevlere hizmet eden çeşitli API’ler sunar. Bunlardan biri olan Admin API, Kafka yapısının yönetimi ve izlenmesi için tasarlanmıştır. Connect API, diğer veri kaynakları ile entegrasyonu sağlar. Producer API mesaj göndermek için kullanılırken, Consumer API basit mesaj okuma işleminde, Kafka Streams API ise daha karmaşık olan gerçek zamanlı veri işleme için kullanılır.
Bu yazımızda Kafka Streams API üzerinde duracağız, Kafka temelleri hakkında detaylı bilgi için Spring ile kafka mesaj alma ve verme blog yazımızı okuyabilirsiniz.
Kafka Streams
Kafka Streams, Kafka platformunun üzerine inşa edilmiş bir akış (stream) işleme kütüphanesi olup, günümüzde birçok sistemin ihtiyaç duyduğu gerçek zamanlı veri işleme ve analiz gereksinimlerini karşılamak amacıyla geliştirilmiştir.
Akış, sınırı olmayan ve sürekli güncellenen veri setidir. Sıralı, fault-tolerant (hataya karşı toleranslı) ve değiştirilemez kayıtlar (record) içerir. Bu kayıtlar ise anahtar (key) ve değer (value) çiftlerinden oluşur.
Her geçen gün daha dijital hale gelen dünyamızda veri akışının artması ve büyük veri setlerinin işlenmesi, özellikle finans, e-ticaret, sosyal medya ve IoT (Internet of Things - Nesnelerin İnterneti) gibi alanlarda kritik bir hale gelmiştir. Kafka Streams kütüphanesi, yüksek performans ve ölçeklenebilirlik sunar. Bu sayede veriler gerçek zamanlı olarak işleyip analiz edilebilir.
Kafka Streams’in genel işleyiş süreci, Kafka topic üzerinde mesajların alınmasını, işlenmesini ve yeniden Kafka topic üzerine yazılmasını içerir. Bu sürecin bütününe topoloji denir ve işlemcilerden oluşur. İşlemci topolojisi (Processor Topology), akışlarla birbirine bağlanan akış işlemcilerinin (stream processor) bir çizgesidir (graph). Bu grafikte kenarlar (edge) akışlardır. Düğümler (node) de işlemcilerdir. Akış işlemcileri verinin işlenmesi sırasındaki bir adımı temsil eder. Topolojilerde iki özel işlemciden bahsedilebilir:
-
Kaynak işlemcisi (Source Processor): Bir veya birden fazla Kafka topic’inden kayıtları tüketir (consume eder) ve kendinden kayıt bekleyen sıradaki işlemcilere iletir. İşlemci topolojisinin başlangıcıdır.
-
Çıkış işlemcisi (Sink Processor): Kendinden önceki işlemcilerden gelen kayıtları belirtilen Kafka topic’ine gönderen (produce eden) işlemcidir. İşlemci topolojisinin sonudur.
Kaynak, çıkış ve diğer işlemcileri içeren Kafka Streams topolojisi Şekil 3’te görselleştirilmiştir.

Verilerin işlenme sürecinde, ihtiyaçlara göre pek çok farklı işlem tanımlanabilir. Bu işlemler, açıklamalarıyla birlikte aşağıda listelenmiştir:
- Filtreleme (Filtering): Belirli kriterlere uyan mesajları seçmek için kullanılır.
- Haritalama (Mapping): Verileri farklı bir forma dönüştürmek için kullanılır.
- Gruplama (Grouping): Benzer verileri bir araya getirmek için kullanılır.
- Toplama (Aggregation): Gruplandırılmış verilerin özetlenmesi veya toplanması için kullanılır.
- Pencereleme (Windowing): Belirli zaman dilimleri içinde verilerin işlenmesi için kullanılır.
- Birleştirme (Joining): İki veya daha fazla veri akışını birleştirmek için kullanılır.
- Ortak Gruplama (Cogrouping): Daha önceden gruplanmış akışların aggregate (toplama) işleminden önce tek bir yerde toplanabilmesini sağlar. Bu akışların anahtarlarının tipi aynı olmalıdır. Ancak değerlerinin tipleri farklı olabilir.”
- Yeniden Bölümleme (Repartitioning): Bir akıştaki kayıtların tekrar partition’lara dağıtılmasını sağlayan bir işlemdir. Manuel olarak tetiklenebilir. Bu durumda partition sayısı değiştirilebilir. Bazı operasyonlar da repartition işlemine sebep olur. Örneğin, kayıtlara anahtarlarını değiştiren bir işlem uygulandığında, partitioning işlemi de anahtarlar ile yapıldığı için bu işlemden sonra kayıtlar yeni anahtarlarına göre yeni partition’lara dağıtılır.
- Dallanma (Branching): Operatör/fonksiyonlar kullanarak bir akışı birden fazla akışa bölme yani dallandırma işlemidir. Fonksiyonlar verilen sıraya göre değerlendirilir ve ilk eşleşmede kayıt ilişkili akışa eklenir. Hiç eşleşme olmazsa varsayılan dala konulur veya varsayılan dal yoksa kayıt silinir.
Topolojideki işlemcilerin her birinde bu işlemlerden biri yapılır. Bunları tanımlamak için 2 yol vardır.
- Kafka Streams DSL(Domain Specific Language - Etki Alanına Özel Dil): İşlemci topolojisini tanımlamanın iki yolundan biridir. High-level (yüksek seviyeli) bir dildir ve map, filter, join ve aggregation gibi sık kullanılan temel işlemleri içerir. Çoğunlukla buradaki işlemler topoloji tanımlamak için yeterli olur.
- Processor API: İşlemci topolojisi tanımlamanın ikinci yoludur ve low-level’dır (düşük seviyeli). Kafka Streams DSL yeterli olmadığında özelleştirilmiş işlemcilerin tanımlanabilmesini sağlar.
İşlemcilerde kullanılan stream’ler, Kafka topic’lerinden okunan mesajlarla oluşur. Bunları okuyup yazmak için Serde’ler kullanılır. Serde, “Serializer/Deserializer” (Serileştirme/Ters Serileştirme) terimlerinin kısaltmasıdır. Kafka topic’lerine okuma veya yazma işleminin yapıldığı operasyonlarda gereklidir. Kayıtların Kafka topic’lerine yazılırken nasıl serialize edileceğini (serileştirme) ve topic’lerden okunurken nasıl deserialize edileceğini (ters serileştirme) içerir. Integer veya String gibi bazı temel tipler için kullanılabilecek hazır Serde’ler bulunur. İhtiyaca özel Serde’ler de oluşturulup kullanılabilir.
Stream API Detayları
KStream: Bir kayıt stream’i soyutlamasıdır. Bağımsız kayıtlarla sınırsız bir veri akışını temsil eder. Tablo yapısı düşünüldüğünde hep insert (ekleme) işlemi yapılıyor gibi düşünülebilir. Anahtar kullanılarak sürekli güncellenebilen kayıtlar içermez. Kredi kartı işlemleri akışı bu şekilde düşünülebilir. Bu işlemler için gelen event stream (olay akışı), kredi kartı numarası ile birlikte kart bakiyesinde yapılacak pozitif veya negatif tutar bilgilerini içerir.
1
{kartId: kart1, bakiyeDeğişimi: +200} -> {kartId: kart1, bakiyeDeğişimi: -100} -> {kartId: kart1, bakiyeDeğişimi: +50}
Yukarıda kredi kartı bakiye değişimi akışı bulunmaktadır. Kart bakiyesinin ilk değeri 0 (sıfır) kabul edilirse son mesajdan sonra 150 olacaktır.
KTable: Bir değişiklik kaydı (changelog) stream’i soyutlamasıdır. Her kayıt bir güncellemeyi ifade eder. Tablodaki veriler, gelen kayıtlardaki anahtarlara göre güncelleniyor gibi düşünülebilir. Eğer anahtar tabloda yoksa eklenir. Varsa değeri güncellenir. Kayıttaki değer null (boş) ise de tablodan silinir. Kredi kartlarının bakiyelerinin tutulduğu bir tablo buna örnek verilebilir. Gelen kayıtlardaki tutarlara göre tablodaki değerler direkt olarak güncellenir.
1
{kartId: kart1, bakiye: 200} -> {kartId: kart1, bakiye: 100} -> {kartId: kart1, bakiye: 150}
Yukarıda kredi kartı işlemi sonrası bakiye değerleri bulunmaktadır. Kart bakiyesi direkt gelen bakiye değeri olarak güncellenir. Son mesajdan sonra kart bakiyesinin 150 olduğu gözlemlenebilir.
KStream ve KTable arasında Şekil 4’te de gözlemlenebileceği gibi bir ilişki (ikililik) bulunur ve ikisi birbirine dönüştürülebilir. KTable’da yapılan her değişiklik KStream’de bir kayıttır. KStream’deki kayıtlar kullanılarak da KTable’daki veriler güncellenip KTable’ın son haline ulaşılabilir.

KStream - KTable Dönüşümü: KStream.toTable ve KTable.toStream operasyonları kullanılarak bu iki yapı birbirine dönüştürülebilir. KStream, KTable’a dönüştürüldüğünde stream’deki her kayıt bir güncelleme olarak algılanır. Yani gelen kayıtlar anahtarı olan bir verinin güncellenmiş son hali olarak işlenir. Tam tersi yapıldığında da kayıtlar artık verinin son hali değil, veri ile alakalı bir olay (event) olarak işlenir.
Örneğin [3] bir topic’i stream olarak okuyup table’a dönüştüren bir Kafka Streams uygulaması olsun ve topic’e şu mesajları gönderilsin:
1
{key: bir, message: 11}, {key: bir, message: 12}, {key: iki, message: 21}, {key: iki, message: 22}
Uygulama çalışıp bu stream bir table’a dönüştürüldükten sonra table’ın yazıldığı topic okunduğunda şu şekilde bir sonuç görülür:
1
{key: bir, message: 12}, {key: iki, message: 22}
GlobalKTable: KTable ile aynı yapıdadır. Örnek olarak 5 partition’ı olan bir topic’in 5 instance’ı (örneği) olan bir uygulama ile KTable’lara okunduğu düşünülebilir. Her instance’taki KTable sadece bir topic için olan mesajları içerir. Ama instance’larda topic GlobalKTable’lara okunuyorsa bunlar tüm partition’lar için olan mesajları içerir. Yani partition fark etmeksizin topic’teki tüm mesajlar, tüm instance’larda olmuş olur. KTable’ların aksine GlobalKTable’larda zaman kavramı yoktur. Anahtarlar kullanılarak GlobalKTable’lardaki güncel kayıtlara erişilebilir.
KGroupedStream, KGroupedTable: groupBy işleminin sonucunda oluşan yapılardır. Kayıtları yapılan gruplama işlemine göre tutarlar. Daha sonra bunlar üzerinde aggregation, count ve reduce gibi işlemler yapılıp sonuç olarak KTable elde edilebilir.
Basit Bir Kod Örneği
Bu bölümde Kafka Streams’i sahada görelim. Aşağıda düz metin akışında kelime tekrar sayılarını hesaplayan bir kod örneğimiz var. olusturKelimeSayanAkis
isimli metodun çalışma adımları:
duz-metin-girdi
isimli bir Kafka topic’den metin satırlarını, KStream objesi olarak okur.- Her metin satırını, boşluk karakterlerine göre kelimelere böler. Küçük harfe çevirerek kelime büyük/küçük harf farklılıklarını giderir.
- Her kelimeyi key olarak kullanır ve verileri gruplar. Her farklı kelime için ayrı bir grup oluşturur.
- Kelimelerin kaç kez tekrarlandığını hesaplar.
- Kelimelerin tekrar sayıları
kelime-sayisi-cikti
isimli Kafka topic’e yazılır. Anahtar kelime, değer ise kelimenin tekrar sayısıdır.
Bu kod örneğini GitHub üzerinden detaylıca inceleyebilirsiniz.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Anahtar: Önemsiz, Değer: Bir satır metin
static final String girdiTopic = "duz-metin-girdi";
// Anahtar: Kelime, Değer: Kelimenin tekrar sayisi
static final String ciktiTopic = "kelime-sayisi-cikti";
static void olusturKelimeSayanAkis(final StreamsBuilder builder) {
// 'duz-metin-akis-girdi' isimli topic girdimiz olacak ve bir KStream oluşturulacak
final KStream<String, String> metinSatirlari = builder.stream(girdiTopic);
// Satırları boşluğa göre bölmek için kullanılacak regex
final Pattern pattern = Pattern.compile("\\W+", Pattern.UNICODE_CHARACTER_CLASS);
// Her metin satırı, boşluk karakterlerine göre kelimelere bölünecek
KStream<String, String> metinSatirlariBoslugaGoreBolunmus = metinSatirlari
.flatMapValues(value -> Arrays.asList(pattern.split(value.toLowerCase())));
// Bölünmüş verileri kelimeye göre gruplanacak. Böylece her kelimenin tekrar sayısı sayılabilecek
// Giriş verilerinin anahtarı değişecek, yeni kayıt anahtarı kelimeler olacak
KGroupedStream<String, String> kelimeyeGoreGruplanmis = metinSatirlariBoslugaGoreBolunmus
.groupBy((_, word) -> word);
// Her kelimenin tekrar sayısı hesplanacak
final KTable<String, Long> kelimeyeTekrarSayilari = kelimeyeGoreGruplanmis
.count();
// Kelime tekrar sayıları, KTable'dan KStream'e döştürülecek
KStream<String, Long> kelimeyeTekrarSayisiAkis = kelimeyeTekrarSayilari.toStream();
// Kelime tekrar sayıları 'kelime-sayisi-cikti' topic'e yazılacak
kelimeyeTekrarSayisiAkis.to(ciktiTopic, Produced.with(Serdes.String(), Serdes.Long()));
}
Kafka Streams Özellikleri
Durum Yönetimi
Kafka streams stateful stream processing (durum bilgisi akış işleme) imkânı sağlar. Yani uygulamanın durum bilgisinin tutmasını ve bu bilginin işlemler sırasında kullanılmasını sağlar. Durum yönetimi oldukça önemli bir özelliktir. Örneğin yukarıda da değindiğimiz toplama, birleştirme veya pencereleme işlemleri sırasında oldukça yararlıdır. Kafka streams, state bilgilerini tutabilmek için state store kullanır. State store’lar key-value veri yapısındadır. Her stream task’ının (görevinin) kendi local state store’u vardır.
State Store Türleri
- Key-Value Store: Anahtar-değer çiftlerini saklar.
- Window Store: Zaman pencerelerine göre verileri saklar.
- Session Store: Oturum bazlı verileri saklar.
Dönüşüm (transformation) işlemleri de state (durum) tutulup tutulmamasına göre ayrılabilir. Stateful (durum tutma) dönüşümlerde durum tutulması gerekirken stateless (durumsuz işlem) dönüşüm işlemlerinin yapılabilmesi için durum tutulmasına gerek yoktur. Örneğin filter işlemi durum tutulmasına ihtiyaç duymaz. Gelen kayıtların bazılarını tutup bazılarını da siler. Ancak count (sayma) işleminin durum tutmaya ihtiyacı vardır. Her key için kaç tane kayıt olduğunu tutması gerekir. Kayıtlar geldikçe de tutulan durumu günceller.
Kafka topic’ine gelen mesajların anahtar bazında sadece en güncel olanlarının saklanması ve eski mesajların silinmesine Log compaction denir. Bir KTable’ın verileri bir Kafka topic’inde tutulmak istenirse bu özellik kullanılabilir. Çünkü bir KTable’da anahtarlar için olan değerlerin sadece son halleri tutulur ve önceki mesajlara ihtiyaç yoktur. Ama bir KStream’in kayıtları bir Kafka topic’inde tutulmak istenirse log compaction kullanılamaz. Bir stream’deki kayıtlar güncellenemez olduğu için önceki kayıtların silinmemesi gerekir.
Örneğin üstteki kredi kartı örneğine göre düşünülürse, KTable için gelen önceki kayıtların tutulmasına gerek olmadığı görülebilir. Çünkü gelen son kayıt zaten kredi kartı bakiyesinin son halini içerir. Ama KStream için olan örnekte tüm kayıtların bulunması gerekir çünkü bakiyenin son haline tüm kayıtlardaki işlemler yapılarak ulaşılabilir. Ayrıca “KStream - KTable Dönüşümü” kısmındaki örneğe de bakılabilir.
Hata Toleransı
Sistemde bir hata meydana geldiğinde veri kaybını önlemek için Kafka Streams’in çeşitli özellikleri vardır.
- State Stores ve Changelog Topics (Durum Saklama Alanı ve Değişiklik Kaydı Topic’leri): Durum bilgisi ve güncellemeler Kafka’da saklanarak veri güvenliği sağlanır.
- Task ve Thread Yeniden Başlatma: Hata durumunda işlemler otomatik olarak yeniden başlatılarak süreklilik korunur.
- Checkpointing (Kontrol Noktası Oluşturma): İşlenen son verinin konumu saklanır, böylece uygulama yeniden başlatıldığında akış kaldığı yerden devam eder.
- Rebalancing (Yeniden Dengeleme): İş yükü dengelenir, akış işlemleri kesintiye uğramadan daha optimize çalışır.
Exactly-Once Semantics (EOS) (Tam Olarak Bir Kez)
Sistemde herhangi bir sıkıntı olduğu zaman, verilerin tutarlı olarak kalması gerekmektedir. İşlenecek verilerin sadece bir kez ele alınması ve çıkan sonucun sadece bir kez yazılması garanti edilmelidir. Sistemin tutarlılığını sağlayan mekanizmalar Tablo 1’de listelenmiştir:
Atomic Writes (Atomik Yazma İşlemi) ve Idempotence (Eş Güçlülük) | Verinin yalnızca bir kez yazılması ve işlenmesi garanti edilir, böylece tekrarlı yazımlar engellenir. |
---|---|
Transactional State Updates (İşlemsel Durum Güncellemeleri) | State store ve Kafka topic güncellemeleri atomik olarak gerçekleştirilir, yani tüm adımlar tek bir işlemde tamamlanır. |
Transaction Coordinator (İşlem Koordinatörü) | İşlemlerin doğru sırayla ve sadece bir kez yapılmasını sağlar, böylece veri tutarlılığı korunur. |
Kafka Streams Konfigürasyonu | "Tam olarak bir kez" garantisi sağlamak için özel yapılandırma ayarları yapılır. |
Dağıtık İşleme
Kafka Streams’in dağıtık işleme yetenekleri, verilerin paralel olarak işlenmesini, iş yükünün dengeli bir şekilde dağıtılmasını ve yüksek güvenilirlik sağlanmasını mümkün kılar. Bu süreç, verilerin Kafka topic’lerinde farklı partition’lara ayrılmasıyla başlar; böylece her bir partition, bağımsız olarak işlenebilir hale gelir. Task Assignment (Görev Atama) mekanizması, her partition için bir görev oluşturur ve bu görevleri Kafka Streams instance’ları arasında dağıtarak işlem yükünü dengeler. Ayrıca, sistemde yeni instance’lar eklendiğinde veya çıkarıldığında, Rebalancing (Yeniden Dengeleme) süreci devreye girerek iş yükünü yeniden optimize eder. Bu esneklik, hem verimli bir dağıtık işleme ortamı sunar hem de yüksek erişilebilirliği garanti eder.
Kafka Streams Güçlü ve Zayıf Yönleri
Kafka Streams, gerçek zamanlı veri işleme dünyasında önemli bir yer tutmaktadır. Ancak her teknolojinin olduğu gibi, Kafka Streams’in de avantajları ve dezavantajları bulunmaktadır.
Artıları
Gerçek Zamanlı Veri İşleme
Kafka Streams, verilerin anında işlenmesine olanak sağlamaktadır. Örneğin, bir e-ticaret sitesinde kullanıcıların alışveriş alışkanlıkları gerçek zamanlı olarak analiz edilerek, onlara anlık ürün önerileri sunulabilmektedir. Bu sayede, kullanıcı deneyiminin kişiselleştirilmesi ve satışların artırılması mümkün olmaktadır. Gerçek zamanlı işleme, finans ve Nesnelerin İnterneti (IoT) gibi anlık verilerin kritik olduğu alanlarda da büyük avantaj sağlamaktadır.
Kafka ile Sıkı Entegrasyon
Kafka Streams, Apache Kafka üzerine inşa edilmiştir ve Kafka’nın tüm avantajlarından yararlanmaktadır. Örneğin, bir finans kurumunda hisse senedi fiyatları gerçek zamanlı olarak işlenip analiz edilebilmektedir. Kafka Streams, Kafka ile sıkı entegrasyonu sayesinde bu verileri anında işlemekte ve yatırımcılara doğru bilgiyi hızlı bir şekilde ulaştırabilmektedir.
Stateless and Stateful Processing (Durumsuz ve Durumlu İşleme)
Kafka Streams, hem durumsuz hem de durumlu veri işleme yeteneklerine sahiptir. Örneğin, stateless işlemlerde, her veri birbirinden bağımsız olarak işlenebilmektedir. Stateful işlemlerde ise bir kullanıcının harcama geçmişi gibi veriler tutulabilmektedir. Bu özellik, karmaşık veri analitiği gerektiren projelerde büyük esneklik sağlamaktadır.
Dağıtık ve Ölçeklenebilir Yapı
Kafka Streams, doğal olarak dağıtık çalışabilmekte ve yatayda kolayca ölçeklenebilir olabilmektedir. Büyük veri kümeleri üzerinde çalışan sosyal medya platformları, milyonlarca kullanıcı verisini anlık olarak işleyip analiz edebilmektedir. Kafka Streams, büyük veri hacmi karşısında performans kaybı yaşamadan çalışarak işletmelere büyük bir operasyonel esneklik sunmaktadır.
Mikroservis Uyumluluğu
Her Kafka Streams uygulaması, bağımsız bir mikroservis olarak yapılandırılabilmektedir. Örneğin, bir e-ticaret platformunda, sipariş yönetimi, kullanıcı davranışları ve stok kontrolü gibi işlemler birbirinden bağımsız olarak mikroservislerle yönetilebilmektedir. Bu da sistemin yönetimini ve bakımını kolaylaştırmaktadır.
Built-in Fault Tolerance (Yerleşik Hata Toleransı)
Kafka Streams, Kafka’nın built-in fault tolerance (yerleşik hata toleransı) özelliğine sahiptir. Örneğin, bir bankacılık uygulaması veri akışı kesilirse veya donanım arızası yaşanırsa, Kafka Streams bu sorunları tolere edebilmekte ve veri kaybı olmadan çalışmaya devam edebilmektedir. Bu özellik, kritik sistemlerde güvenilirlik artırmaktadır.
Eksileri
Karmaşık Yapı
Kafka Streams’in learning curve (öğrenme eğrisi) oldukça dik olabilmektedir. Daha önce Kafka veya dağıtık sistemler ile çalışmamış bir geliştirici, basit bir veri işleme senaryosunu bile uygulamakta zorlanabilmektedir. Daha karmaşık veri işleme ihtiyaçları olduğunda, uygulamanın yapılandırılması daha da zorlaşabilmektedir.
Yetersiz Dokümantasyon ve Topluluk Desteği
Kafka Streams, bazı diğer data processing tools (veri işleme araçlarına) göre daha az yaygın bir community support (topluluk desteğine) sahiptir. Karşılaşılan teknik sorunları çözmek için kaynak bulmak bazen zor olabilmektedir. Özellikle nadir görülen hatalarla karşılaşıldığında, çözüm bulma süreci uzayabilmektedir.
Yüksek Kaynak Kullanımı
Kafka Streams, low latency (düşük gecikme) sağlamak için sistem kaynaklarını yoğun bir şekilde kullanabilmektedir. Büyük veri setleriyle çalışılırken, CPU ve memory usage (bellek kullanımında) artışlar gözlemlenebilmektedir. Bu da özellikle bulut tabanlı uygulamalarda maliyetlerin yükselmesine neden olabilmektedir.
Özel İşlemler İçin Esneklik Eksikliği
Kafka Streams, birçok standart veri işleme senaryosunu desteklemektedir; ancak bazı özel durumlarda esneklik sağlayamamaktadır. Örneğin, çok spesifik iş kurallarının uygulanması gerektiğinde Kafka Streams bu ihtiyaçları karşılamakta yetersiz kalabilmektedir. Bu gibi durumlarda, manuel olarak kodlama yapılması veya başka bir araç kullanılması gerekebilmektedir.
Kafka Streams Kullanım Senaryoları
Apache Kafka ve Kafka Streams API, birçok büyük şirket tarafından kullanılmaktadır. Örneğin:
- Bir görsel arama motoru olan Pinterest’in altyapısında, reklamların bütçe yönetimini yapan sistemde kullanılıyor.
- Hollanda merkezli Rabobank’ta finansal değişiklikler hakkında müşterilerini uyaran sistemde kullanılıyor.
- Amerikan gazetesi The New York Times’ın, iç sistemlerinde yayınlanan içeriklerin depolanması ve dağıtılması için kullanılıyor.
Yazının devamında New York Times’ın Publishing Pipeline (Yayınlama Hattı) adlı yaklaşımını inceleceğiz.
The New York Times’ın Yayınlama Hattı
The New York Times (NYT), en ünlü haber kaynaklarından biridir. Bu ünü koruyabilmek için yayın sistemi altyapısını güncel tuttuğu söylenebilir. NYT yayın mimarisi iki parçaya ayrılabilir: İçerik üreticileri ve tüketicileri. İçerik üreticileri NYT’nin geniş içerik havuzu oluşturuyor. Örnek olarak içerik yönetim sistemleri, haber ajansları, üçüncü taraf verileri ve içerik arşivleri verilebilir. İçerik tüketicileri ise birçok farklı hizmeti, çeşitli uygulama aracılığıyla okuyucularına ulaştırıyor. Örnek olarak arama motorları, kişiselleştirme hizmetleri, web siteleri ve mobil uygulamalar verilebilir.
Şekil 5’te görülebileceği üzere NYT’nin mimarisi API tabanlıydı. Bu mimaride, içerik üreticileri ve tüketicileri arasındaki bilgi alışverişi, her bir üretici ve tüketici arasında birebir yapılıyordu. Ancak bu mimari, içerik üretimi ve tüketimi arasında koordinasyon ve performans sorunlarına yol açtı. NYT, bu sorunları çözmek için yeni bir mimariye geçme kararı aldı.

Yeni mimari Şekil 6’da gözlemlenebilir. Merkezinde Kafka teknolojisi yer alır ve kayıt tabanlıdır. Bu mimari, farklı kaynaklardan gelen verilerin ortak bir kayıt formunda, tek bir yerde tutulmasını esas alır.
NYT için:
- Kaynaklar, içerik üretici sistemlerdir.
- Kayıtlar, yayınlanmış içerikleri temsil eder.
- Kayıtların tutulduğu yer ise Kafka Topic’leridir.
İçerik tüketici sistemler, bu topic’leri dinleyerek ihtiyaç duydukları içeriğe kolayca erişebilir. NYT farklı gereksinimlere sahip içerik tüketici sistemler için Kafka Streams kütüphanesinden yardım almıştır.

Farklı gereksinimlere sahip içerik tüketici sistemlerini açıklamak için basit bir senaryo inceleyeceğiz. Senaryomuz, içerik yönetim sisteminden yayınlanan bir içeriğin, arama hizmetinde kullanılmak üzere Elasticsearch (Açık kaynaklı bir arama ve analiz motoru) düğümüne ulaştırılmasını kapsamaktadır ve Şekil 7’de görselleştirilmiştir.:
- İçerik yönetim sisteminden çıkan yayınlanmış içerik Monolog isimli Topic’e yazılır. Monolog verileri normalize yapıdadır. Normalize veri, bağımlılıkları referans olarak içerirken, denormalize veri bağımlılıkları da içinde barındırır. Bazı tüketiciler normalize veriyi doğrudan kullanabilirken, arama algoritmalarında bu veri tipi uygun değildir.
- Monolog topic’inden alınan veriye denormalizasyon işlemi uygulanır ve bu veri Denormalizedlog ismli Topic’e aktarılır. Bu işlem, Kafka Streams kütüphanesini kullanan bir Java uygulaması aracılığıyla gerçekleştirilir.
- Denormalizedlog isimli Topic’den alınan veri uygun JSON formatına dönüştürülür ve Elasticsearch düğümüne iletilir. Bu işlem de Kafka Streams kütüphanesini kullanan uygulamalar aracılığıyla yapılır.

Sonuç
Kafka Streams, günümüzün gerçek zamanlı veri işleme ihtiyaçlarını karşılamakta olan güçlü bir çözüm olarak öne çıkmaktadır. “Neden Kafka Streams’e ihtiyaç duyulmaktadır?” sorusuna bakıldığında, veri hacminin ve hızının sürekli arttığı bir ortamda, olay tabanlı mimarilerde verilerin anlık işlenmesi zorunlu hale gelmektedir. Geleneksel veri işleme yöntemleri bu hız ve esneklik gereksinimine cevap veremediği için, Kafka Streams gibi araçlar bu boşluğu doldurmaktadır.
Kafka Streams’in bu ihtiyaca nasıl yanıt verdiği incelendiğinde, basit API’leri ile geliştir için güçlü ve esnek bir araç olduğu görülmektedir. Yerel durumsal işleme yetenekleri ve dağıtık yapısı sayesinde Kafka Streams, büyük veri kümelerinin ölçeklenebilir bir şekilde işlenmesini mümkün kılmaktadır. Ayrıca, mikroservis mimarilerine kolayca entegre olabilmesi, Kafka Streams’i modern uygulama geliştirme süreçlerinde vazgeçilmez bir araç haline getirmektedir
Kafka Streams’in, büyük ölçekli ve gerçek zamanlı veri işleme dünyasında neden bu kadar önemli olduğu ve nasıl etkin bir çözüm sunduğu açıkça görülmektedir. Bu güçlü araç, veri akışlarının hızlı ve verimli bir şekilde işlenmesini isteyen işletmelere büyük avantajlar sağlamaktadır.
Kaynakça
[1] Apache Software Foundation, “Apache Kafka Streams.” https://kafka.apache.org/documentation/streams
[2] Confluent, “Kafka streams for Confluent platform.” https://docs.confluent.io/platform/current/streams/overview.html
[3] “How to convert a Kafka Streams KStream to a KTable using Confluent.” https://developer.confluent.io/tutorials/kafka-streams-convert-to-ktable/confluent.html
[4] N. Narkhede, G. Shapira, and T. Palino, KAFKA: The Definitive Guide: Real-Time Data and Stream Processing At Scale. 2017.
[5] M. Noll, Kafka Streams: What’s New and Coming Next. Confluent Blog, 2022.
[6] B. Svingen, “Publishing with Apache Kafka at The New York Times,” Medium, Jan. 23, 2020. [Online]. Available: https://open.nytimes.com/publishing-with-apache-kafka-at-the-new-york-times-7f0e3b7d2077
[7] Pinterest Engineering, “Using Kafka Streams API for predictive budgeting,” Medium, Aug. 16, 2019. [Online]. Available: https://medium.com/pinterest-engineering/using-kafka-streams-api-for-predictive-budgeting-9f58d206c996
[8] A. Woodie, “How Kafka helped Rabobank modernize alerting System,” BigDATAwire, Aug. 17, 2017. https://www.datanami.com/2017/08/15/kafka-helped-rabobank-modernize-alerting-system
[9] D. Arneam, “Kafka Streams Concepts,” Oct. 25, 2020. https://daniel.arneam.com/blog/distributedarchitecture/2020-10-25-Kafka-Streams-Concepts
Yazımızın teknik gözden geçirmesi için Sena Berre CEYLAN’a, editör desteği için ise Kübra ERTÜRK’e teşekkür ederiz.