Kafka Topic, Partition ve Consumer Group Kavramları

Dağıtık sistemlerle ilk kez çalışmaya başladığımda Kafka bana sihir gibi geldi. Mesajlar bir yerden bir yere gidiyor, sistemler birbirinden habersiz çalışıyor, her şey ölçekleniyor. Ama bu sihrin arkasında çok net kavramlar var: topic, partition ve consumer group. Bu üçlüyü anlamadan Kafka kurulumu yapabilirsiniz, evet, ama neden böyle davrandığını asla kavrayamazsınız. Şimdi bu kavramları üretim ortamında karşılaştığım gerçek durumlar üzerinden açıklayacağım.

Topic Nedir, Neden Bu Kadar Önemli?

Kafka’da topic, mesajların organize edildiği mantıksal birimdir. Veritabanındaki tablo gibi düşünebilirsiniz ama burada okuma işlemi mesajı silmez, veriler retention policy’ye göre tutulur. Bu fark çok kritik.

Bir e-ticaret platformu düşünün. Sipariş olayları için orders, kullanıcı davranışları için user-events, envanter güncellemeleri için inventory-updates şeklinde ayrı topicler açarsınız. Her bir topic, ilgili veri akışının sahibidir.

Topic oluşturmak basit görünür ama parametreler kritik:

# Temel topic oluşturma
kafka-topics.sh --create 
  --bootstrap-server localhost:9092 
  --topic orders 
  --partitions 6 
  --replication-factor 3

# Topic detaylarını görme
kafka-topics.sh --describe 
  --bootstrap-server localhost:9092 
  --topic orders

Çıktı şöyle görünür:

Topic: orders   PartitionCount: 6   ReplicationFactor: 3   Configs: segment.bytes=1073741824
    Topic: orders   Partition: 0    Leader: 2   Replicas: 2,0,1   Isr: 2,0,1
    Topic: orders   Partition: 1    Leader: 0   Replicas: 0,1,2   Isr: 0,1,2
    Topic: orders   Partition: 2    Leader: 1   Replicas: 1,2,0   Isr: 1,2,0
    Topic: orders   Partition: 3    Leader: 2   Replicas: 2,1,0   Isr: 2,1,0
    Topic: orders   Partition: 4    Leader: 0   Replicas: 0,2,1   Isr: 0,2,1
    Topic: orders   Partition: 5    Leader: 1   Replicas: 1,0,2   Isr: 1,0,2

Buradan şunu okuyabiliyorsunuz: her partition’ın bir leader broker’ı var, veriler birden fazla broker’da replike ediliyor ve ISR (In-Sync Replicas) listesi o an senkronize olan kopyaları gösteriyor.

Partition Mimarisi: Kafka’nın Kalbi

Partition kavramını iyi anlamak, Kafka’daki neredeyse tüm tasarım kararlarını netleştirir.

Partition bir ordered log’dur. Mesajlar partition’a sırayla yazılır ve offset numarasıyla adreslenir. Offset 0’dan başlar ve sürekli artar. Bir partition içinde sıralama garantisi kesindir. Birden fazla partition arasında ise sıralama garantisi yoktur.

Bu ayrım pratikte çok önemli. Aynı kullanıcıya ait olayların sırayla işlenmesini istiyorsanız, o kullanıcının tüm mesajlarının aynı partition’a gitmesini sağlamalısınız. Bunu message key ile yaparsınız:

# Producer tarafında key kullanımı (Python kafka-python ile)
from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers=['kafka1:9092', 'kafka2:9092', 'kafka3:9092'],
    key_serializer=lambda k: k.encode('utf-8'),
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# user_id key olarak kullanılıyor
# Aynı user_id her zaman aynı partition'a gider
producer.send(
    topic='user-events',
    key=str(user_id),
    value={
        'user_id': user_id,
        'event': 'purchase',
        'amount': 299.90,
        'timestamp': '2024-01-15T14:30:00Z'
    }
)
producer.flush()

Key olmadan gönderilen mesajlar round-robin dağılır. Key varsa, key’in hash’i alınır ve partition sayısına bölünür, kalan sayı partition numarasını belirler. Bu deterministik bir hesaplama olduğu için aynı key her zaman aynı partition’a düşer.

Partition Sayısı Nasıl Belirlenir?

En çok sorulan soru budur ve maalesef evrensel bir cevabı yok. Ama birkaç pratik kural var:

  • Throughput hedefi: Tek bir partition’ın saniyede işleyebildiği mesaj miktarını ölçün. Toplam throughput hedefinizi buna bölün.
  • Consumer sayısı: Bir partition’ı aynı anda sadece bir consumer okuyabilir. Dolayısıyla maksimum paralel consumer sayısı = partition sayısı.
  • Büyüme planı: Partition sayısını artırmak mümkün ama sonradan artırmanın yan etkileri var. Başlangıçta biraz fazla açmak genellikle daha iyidir.

Bizim üretim ortamımızda genellikle şu formülü kullanıyoruz: hedef throughput / tek consumer throughput = minimum partition sayısı, sonra bu sayıyı 2 ile çarpıp biraz üstüne çıkıyoruz.

# Mevcut topic'e partition ekleme (azaltmak mümkün değil!)
kafka-topics.sh --alter 
  --bootstrap-server localhost:9092 
  --topic orders 
  --partitions 12

# Partition sayısını listele
kafka-topics.sh --list 
  --bootstrap-server localhost:9092

Önemli uyarı: Partition sayısını artırdığınızda, key-based routing bozulur. Daha önce partition 3’e giden user_id=42’nin mesajları artık farklı bir partition’a düşebilir. Eğer sıralama kritikse, bu geçiş dönemini dikkatli yönetmeniz gerekir.

Consumer Group: Ölçeklemenin Sırrı

Consumer group, Kafka’nın en güçlü özelliğidir. Birden fazla consumer’ın bir topic’i paralel olarak okumasını ve her mesajın sadece bir kez işlenmesini sağlar.

Konsepti somutlaştıralım. Diyelim ki 6 partition’lı bir orders topicimiz var ve 3 consumer’dan oluşan order-processor grubumuz var. Kafka bu durumda her consumer’a 2 partition assign eder. Her consumer kendi partition’larını okur, aynı mesajı iki consumer birden okumaz.

# Consumer group durumunu görme
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --group order-processor 
  --describe

Çıktı kritik bilgiler içerir:

GROUP           TOPIC     PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG  CONSUMER-ID                    HOST
order-processor orders    0          15420           15420           0    consumer-1-uuid-xxx            /10.0.1.10
order-processor orders    1          12300           12305           5    consumer-1-uuid-xxx            /10.0.1.10
order-processor orders    2          18900           18900           0    consumer-2-uuid-yyy            /10.0.1.11
order-processor orders    3          9800            9800            0    consumer-2-uuid-yyy            /10.0.1.11
order-processor orders    4          22100           22100           0    consumer-3-uuid-zzz            /10.0.1.12
order-processor orders    5          17600           17600           0    consumer-3-uuid-zzz            /10.0.1.12

LAG sütunu burada en kritik metrik. Partition 1’de 5 mesajlık lag var, yani consumer bu partition’da 5 mesaj geride. Bu düşük bir değer, alarm vermeyi gerektirmiyor. Ama bu değer binlere ulaşıyorsa consumer’ınız mesaj üretimine yetişemiyor demektir.

Rebalancing: İyi Anlaşılması Gereken Bir Süreç

Consumer group’a yeni bir consumer eklendiğinde veya bir consumer düştüğünde rebalancing gerçekleşir. Bu süreçte partition atamaları yeniden yapılır ve kısa bir süre boyunca hiçbir consumer mesaj okuyamaz. Bu “stop-the-world” etkisine dikkat etmek gerekir.

# Java/Python consumer örneği - rebalance listener ile
from kafka import KafkaConsumer
from kafka.coordinator.assignors.roundrobin import RoundRobinPartitionAssignor

consumer = KafkaConsumer(
    'orders',
    bootstrap_servers=['kafka1:9092', 'kafka2:9092', 'kafka3:9092'],
    group_id='order-processor',
    auto_offset_reset='earliest',
    enable_auto_commit=False,
    max_poll_interval_ms=300000,  # 5 dakika - uzun işlemler için
    session_timeout_ms=30000,
    heartbeat_interval_ms=10000,
    partition_assignment_strategy=[RoundRobinPartitionAssignor]
)

for message in consumer:
    try:
        process_order(message.value)
        # Manuel commit - işlem başarılıysa
        consumer.commit()
    except Exception as e:
        # Hata durumunda commit yapma
        log.error(f"Failed to process message: {e}")
        raise

enable_auto_commit=False kullanmak neden önemli? Otomatik commit açıkken, mesajı aldınız ama işlem sırasında hata oluştuysa mesaj “tüketildi” sayılır ve kaybolur. Manuel commit ile önce işlemi tamamlarsınız, sonra commit edersiniz. At-least-once semantics için bu şarttır.

Consumer Sayısı ile Partition Sayısı İlişkisi

Bu ilişkiyi doğru kurmak performansın temelidir:

  • Consumer sayısı < Partition sayısı: Bazı consumer’lar birden fazla partition okur. Kabul edilebilir bir durum.
  • Consumer sayısı = Partition sayısı: İdeal durum. Her consumer tam olarak bir partition’dan okur.
  • Consumer sayısı > Partition sayısı: Fazla consumer’lar idle bekler. Kaynak israfı ve işe yaramaz durum.
# Tüm consumer group'ları listele
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --list

# Lag monitoring için script
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --group order-processor 
  --describe | awk 'NR>1 {sum += $6} END {print "Total LAG:", sum}'

Offset Yönetimi: Nerede Kaldım?

Her consumer, her partition için son okuduğu pozisyonu (offset) takip eder. Bu bilgi Kafka’nın __consumer_offsets adlı dahili topic’inde saklanır.

Offset yönetimi çok kritik senaryolarda gündeme gelir. Uygulamanızı güncellediniz, bir bug düzelttiniz ve bazı mesajları yeniden işlemeniz gerekiyor. Ya da disaster recovery sonrası belirli bir noktadan devam etmek istiyorsunuz.

# Consumer group'u durdur
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --group order-processor 
  --reset-offsets 
  --to-earliest 
  --topic orders 
  --execute

# Belirli bir offset'e geri dön
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --group order-processor 
  --reset-offsets 
  --to-offset 5000 
  --topic orders:2 
  --execute

# Belirli bir zamana geri dön
kafka-consumer-groups.sh 
  --bootstrap-server localhost:9092 
  --group order-processor 
  --reset-offsets 
  --to-datetime 2024-01-15T00:00:00.000 
  --topic orders 
  --execute

Kritik not: --execute flag’i olmadan sadece simülasyon yaparsınız, gerçek işlem yapmaz. Offset reset işlemi geri alınamaz, dikkatli olun.

Gerçek Dünya Senaryosu: Birden Fazla Consumer Group

Aynı topic’i farklı amaçlarla farklı sistemler tüketebilir. Bu, Kafka’nın pub-sub modelinin en güçlü yanıdır.

Düşünün: orders topicinden hem sipariş işleme servisi, hem e-posta bildirimi servisi, hem de analitik pipeline okusun. Her biri bağımsız bir consumer group olarak çalışır ve birbirinden tamamen bağımsız offset takip eder.

# Farklı amaçlar için farklı consumer group'lar
# Her grup aynı topic'i bağımsız tüketir

# Grup 1: Sipariş işleme
kafka-consumer-groups.sh --bootstrap-server localhost:9092 
  --group order-processor --describe

# Grup 2: Email bildirimleri
kafka-consumer-groups.sh --bootstrap-server localhost:9092 
  --group email-notification-service --describe

# Grup 3: Analitik
kafka-consumer-groups.sh --bootstrap-server localhost:9092 
  --group analytics-pipeline --describe

Email servisi yavaş mı çalışıyor? Sadece o grubun lag’ı artar, sipariş işleme bundan etkilenmez. Her grup kendi hızında, kendi offset’iyle ilerler.

Retention ve Log Compaction

Topic’lerde mesajlar sonsuza kadar saklanmaz. İki tür retention mekanizması vardır:

Time-based retention: Mesaj belirli bir süreden sonra silinir.

Size-based retention: Topic belirli bir boyutu geçince eski mesajlar silinir.

# Topic retention ayarları
kafka-configs.sh --bootstrap-server localhost:9092 
  --alter 
  --entity-type topics 
  --entity-name orders 
  --add-config retention.ms=604800000  # 7 gün (ms cinsinden)

# Size-based retention
kafka-configs.sh --bootstrap-server localhost:9092 
  --alter 
  --entity-type topics 
  --entity-name orders 
  --add-config retention.bytes=10737418240  # 10 GB per partition

# Mevcut konfigürasyonu görüntüle
kafka-configs.sh --bootstrap-server localhost:9092 
  --describe 
  --entity-type topics 
  --entity-name orders

Log compaction farklı bir yaklaşım. Özellikle “son durum” önemli olan durumlarda kullanılır. Aynı key’e sahip mesajlardan sadece en son olanı saklanır. Kullanıcı profil güncellemeleri, ürün stok durumu gibi senaryolar için idealdir.

Performans Tuning: Üretimde Dikkat Edilecekler

Kafka’yı sadece kurmak yetmez. Doğru parametrelerle çalıştırmak gerekir. Üretim ortamında karşılaştığım birkaç kritik ayar:

# Producer performans ayarları
# server.properties veya producer config dosyasında

# Batch boyutu - daha büyük batch = daha iyi throughput
batch.size=65536  # 64 KB

# Linger - mesaj göndermeden önce bekle (batch'i doldurma şansı ver)
linger.ms=5

# Compression
compression.type=snappy

# Consumer poll ayarları
max.poll.records=500     # Tek seferinde maksimum 500 mesaj
fetch.min.bytes=1048576  # En az 1MB veri bekle
fetch.max.wait.ms=500    # En fazla 500ms bekle

Lag monitoring için Prometheus ve Grafana entegrasyonu şart. Ama basit bir shell scripti ile de lag takibi yapılabilir:

#!/bin/bash
# lag-monitor.sh
BROKER="localhost:9092"
GROUP="order-processor"
TOPIC="orders"
THRESHOLD=10000

LAG=$(kafka-consumer-groups.sh 
  --bootstrap-server $BROKER 
  --group $GROUP 
  --describe 2>/dev/null | 
  grep $TOPIC | 
  awk '{sum += $6} END {print sum}')

if [ "$LAG" -gt "$THRESHOLD" ]; then
    echo "ALERT: Consumer group $GROUP lag is $LAG (threshold: $THRESHOLD)"
    # Buraya alerting mekanizmanızı ekleyin
    exit 1
fi

echo "OK: Consumer group $GROUP lag is $LAG"
exit 0

Bu script’i crontab’a veya monitoring sisteminize ekleyebilirsiniz.

Sık Yapılan Hatalar

Yıllar içinde gördüğüm yaygın hatalar:

  • Partition sayısını çok düşük açmak: Sonradan artırmak sorun yaratır, başlangıçta biraz cömert olun.
  • Auto-commit açık bırakmak: Mesaj kaybına yol açar. Production’da mutlaka manuel commit kullanın.
  • Replication factor’ı 1 bırakmak: Bir broker düştüğünde veri kaybedersiniz. En az 3 kullanın.
  • Consumer’ların session timeout’unu çok kısa ayarlamak: Sık rebalance’e yol açar. Uzun işlemler için max.poll.interval.ms değerini artırın.
  • Aynı group.id’yi farklı amaçlarla kullanmak: Her consumer uygulaması için benzersiz group.id kullanın.

Sonuç

Kafka’nın topic, partition ve consumer group üçlüsü, sistemin tüm ölçekleme ve güvenilirlik özelliklerinin temelini oluşturur. Partition’lar paralel işlemeyi mümkün kılar, consumer group’lar iş yükünü dağıtır, offset mekanizması tam olarak nereden devam edeceğinizi bilmenizi sağlar.

Bu kavramları iyi anladığınızda, Kafka cluster’ınızın davranışını tahmin edebilir, kapasiteyi doğru planlayabilir ve sorunları çok daha hızlı teşhis edebilirsiniz. Kafka sihir değil, üzerine düşünülmüş tasarım kararlarının toplamıdır. O tasarım kararlarını anladığınızda sistem de size mantıklı gelmeye başlar.

Lag artışı, rebalance fırtınası, partition dengesizliği gibi sorunlarla karşılaştığınızda yukarıdaki komutları araç kutusu olarak kullanın. Veriye bakın, ölçün, karar verin. Kafka’da tahmin yürütmek yerine --describe çıktısına bakmak her zaman daha doğru yola çıkarır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir