12 Topics, Partitionen und Replikation

Die interne Struktur von Apache Kafka basiert auf drei fundamentalen Konzepten: Topics als logische Event-Streams, Partitionen als Skalierungseinheiten und Replikation für Ausfallsicherheit. Das Verständnis dieser Konzepte ist entscheidend für effektives Consumer-Design und Systemskalierung.

12.1 Konzeptionelle Grundlagen

Ein Topic stellt einen benannten Event-Stream dar, vergleichbar mit einer Tabelle in einer Datenbank oder einem Ordner im Dateisystem. Jedes Topic wird physisch in mehrere Partitionen aufgeteilt, die jeweils eine geordnete, unveränderliche Sequenz von Events enthalten. Diese Partitionierung ermöglicht sowohl horizontale Skalierung als auch parallele Verarbeitung.

Jede Partition funktioniert wie ein Append-Only-Log. Events werden immer am Ende hinzugefügt und erhalten eine eindeutige, aufsteigende Offset-Nummer. Innerhalb einer Partition ist die Reihenfolge der Events garantiert, zwischen verschiedenen Partitionen jedoch nicht.

Die Replikation sorgt für Datensicherheit und Verfügbarkeit. Jede Partition wird auf mehrere Broker-Instanzen kopiert. Eine Partition fungiert als “Leader” und verarbeitet alle Read/Write-Operationen, während die anderen als “Follower” synchron mitlaufen.

Der Partitionierungs-Key bestimmt, in welche Partition ein Event geschrieben wird. Events mit demselben Key landen garantiert in derselben Partition und behalten somit ihre Reihenfolge. Ohne expliziten Key werden Events per Round-Robin auf alle Partitionen verteilt.

// Producer mit explizitem Partitionierungs-Key
kafkaTemplate.send("order.placed.v1", 
                   order.getCustomerId(),  // Key -> gleiche Partition
                   orderEvent);

// Producer ohne Key -> Round-Robin-Verteilung  
kafkaTemplate.send("order.placed.v1", orderEvent);

12.2 Auswirkungen auf Consumer-Design

Die Partitionierung beeinflusst fundamental, wie Consumer implementiert werden müssen. Ein einzelner Consumer kann mehrere Partitionen verarbeiten, aber eine Partition wird immer nur von einem Consumer innerhalb einer Consumer Group verarbeitet.

Consumer Groups ermöglichen horizontale Skalierung. Mehrere Consumer-Instanzen teilen sich die Partitionen eines Topics auf. Fällt ein Consumer aus, werden seine Partitionen automatisch auf die verbleibenden Consumer umverteilt. Dieser Mechanismus heißt “Rebalancing” und erfolgt transparent.

@KafkaListener(topics = "order.placed.v1", 
               groupId = "payment-service")
public void handleOrderPlaced(OrderPlacedEvent event) {
    // Dieser Consumer verarbeitet eine oder mehrere Partitionen
    // aber keine Partition wird von mehreren Consumern verarbeitet
}

Die Anzahl der Partitionen definiert die maximale Parallelität. Hat ein Topic vier Partitionen, können maximal vier Consumer einer Group parallel arbeiten. Ein fünfter Consumer würde idle bleiben.

Offset-Management erfolgt auf Partitionsebene. Jeder Consumer merkt sich pro Partition, bis zu welchem Offset er Events verarbeitet hat. Diese Information wird in einem speziellen Kafka-Topic (__consumer_offsets) persistiert.

Bei der Fehlerbehandlung muss berücksichtigt werden, dass das Überspringen eines fehlerhaften Events den Offset für die gesamte Partition beeinflusst. Kann ein Event nicht verarbeitet werden, blockiert dies alle nachfolgenden Events in derselben Partition.

# Python Consumer mit manueller Offset-Kontrolle
consumer = Consumer({
    'bootstrap.servers': 'localhost:9092',
    'group.id': 'payment-service',
    'enable.auto.commit': False  # Manuelles Offset-Management
})

while True:
    msg = consumer.poll(1.0)
    if msg:
        try:
            process_event(msg.value())
            consumer.commit(msg)  # Offset nur bei Erfolg committen
        except Exception as e:
            handle_error(msg, e)
            # Entscheidung: Skip oder Retry?

12.3 Skalierung und Verfügbarkeit

Die Partition-Anzahl bestimmt die maximale Consumer-Parallelität und sollte bei der Topic-Erstellung wohlüberlegt gewählt werden. Zu wenige Partitionen limitieren die Skalierung, zu viele erhöhen den Overhead. Eine Faustformel ist: Anzahl Partitionen = erwartete maximale Consumer-Anzahl.

Partitionen können nachträglich nur erhöht, nie verringert werden. Diese Entscheidung ist irreversibel, da eine Verringerung die Verteilung bestehender Events ändern würde.

Partitionen Max. Parallele Consumer Overhead Empfehlung
1 1 Minimal Nur für niedrige Volumina
3-6 3-6 Gering Standard für mittlere Systeme
10-30 10-30 Moderat High-Volume Anwendungen
>50 >50 Hoch Nur bei extremen Anforderungen

Replikation erfolgt über den replication.factor. Ein Faktor von 3 bedeutet, dass jede Partition auf drei verschiedenen Brokern gespeichert wird. Fällt ein Broker aus, übernimmt automatisch ein Follower die Leader-Rolle für seine Partitionen.

Die In-Sync Replica (ISR) Liste enthält alle Follower, die mit dem Leader synchron sind. Nur ISR-Mitglieder können zum neuen Leader gewählt werden. Der Parameter min.insync.replicas definiert, wie viele Replikas mindestens synchron sein müssen, damit Writes akzeptiert werden.

Availability vs. Consistency kann über Producer-Konfiguration gesteuert werden. Der acks-Parameter definiert, wann ein Write als erfolgreich gilt:

// Höchste Verfügbarkeit, niedrigste Konsistenz
producer.setProperty("acks", "0");  // Fire-and-forget

// Ausgewogenes Verhältnis  
producer.setProperty("acks", "1");  // Leader bestätigt

// Höchste Konsistenz, niedrigere Verfügbarkeit
producer.setProperty("acks", "all"); // Alle ISR bestätigen

Rack Awareness sorgt dafür, dass Replikas verschiedener Partitionen auf Broker in unterschiedlichen Racks oder Availability Zones verteilt werden. Dies erhöht die Ausfallsicherheit bei Hardware- oder Netzwerkproblemen.

Die richtige Balance zwischen Partitionierung und Replikation hängt von den spezifischen Anforderungen ab. Mehr Partitionen ermöglichen höhere Parallelität, aber erhöhen den Verwaltungsaufwand. Höhere Replikation bietet bessere Ausfallsicherheit, verbraucht aber mehr Speicher und Netzwerk-Bandbreite.

Für EDA-Einsteiger empfiehlt sich ein konservativer Ansatz: 3-6 Partitionen pro Topic mit einem Replikationsfaktor von 3. Diese Konfiguration bietet ausreichende Skalierbarkeit für die meisten Anwendungsfälle ohne übermäßige Komplexität.