Proč se Kafka liší od tradiční Coda

Při návrhu distribuovaného systému, který potřebuje zpracovávat proudy událostí v reálném čase, je prvním pokušením použít klasickou frontu zpráv jako RabbitMQ nebo ActiveMQ. Tato řešení fungují dobře pro jednoduché scénáře, ale mají klíčové strukturální omezení: jakmile spotřebitel pokud zprávu spotřebuje, zpráva je smazána. Není zde možnost si to znovu přečíst, mít více nezávislých spotřebitelů, kteří to zpracují jinak, nebo přehrát celou historii událostí.

Apache Kafka se narodil v roce 2011 na LinkedIn s radikálně odlišnou filozofií: zprávami (tzv záznam) jsou zapsány v a log append-only a zůstanou tam po nastavitelnou dobu (výchozí: 7 dní). Různí spotřebitelé mohou občas číst stejné záznamy různé, každý sleduje svou polohu prostřednictvímoffset. Tento vzorec promění Kafku v mnohem víc než jen frontu: se stává zdroj pravdy pro celou historii událostí vašeho systému.

Kafka v roce 2026: Klíčová čísla

  • Používá se přes 80 % společností Fortune 500 pro případy použití streamování
  • Kafka 4.0 (březen 2025) trvale odstranil ZooKeeper a přesunul se na Kraft
  • Teoretická propustnost: 1 milion a více zpráv za sekundu pro makléře (komoditní hardware)
  • Confluent Cloud: spravovaný Kafka dostupný na AWS, GCP, Azure s latencí < 10 ms p99
  • Ekosystém: 200+ konektorů přes Kafka Connect, Kafka Streams, integrace Apache Flink

Základní model: Broker, téma a oddíl

Broker: The Cluster Node

Un makléř je to prostě Kafka server. Kafka cluster se skládá z jednoho nebo více brokerů, z nichž každý je označen a broker.id unikátní. Ve výrobě se obvykle používá 3, 6 nebo 9 makléřů, aby byla zaručena odolnost proti chybám. O zápis se starají makléři a čtení záznamů, udržování protokolů na disku a replikace mezi uzly.

S Kafkou 4.0 a novým způsobem KRaft přebírá roli také jeden nebo více brokerů ovladač, spravující metadata clusteru (kdo je vůdcem kterého oddílu, kteří brokeři jsou aktivní atd.) prostřednictvím interního protokolu konsensu Raft. Už není potřeba samostatného souboru ZooKeeper.

Téma: Logická kategorie záznamů

Un téma je logický název, pod kterým výrobci vydávají záznamy a spotřebitelé je čtou. Můžete si to představit jako tematický kanál: ordini-effettuati, pagamenti-confermati, eventi-utente. Každé téma má svou vlastní konfiguraci pro uchovávání, počet oddílů, faktor replikace a zásady komprimace.

Témata jsou rozdělené: každé téma je rozděleno do N fyzických oddílů, které jsou rozděleny mezi makléře. Je to tato distribuce díky čemuž je Kafka horizontálně škálovatelný pro psaní i čtení.

Partition: Jednota paralelismu a uspořádání

Una rozdělit je to přehledný a neměnný protokol pouze pro připojení. Každý záznam zapsaný do oddílu obdrží a offset monotónně rostoucí (0, 1, 2, ...). Třídění je zaručeno uvnitř oddílu, nikoli mezi různými oddíly.

Rozdělení záznamů mezi oddíly je určeno klíč oddílu: Pokud výrobce specifikuje klíč, záznam vždy přejde do stejného oddílu (hash klíče modulu počtu oddílů), což zaručuje řazení pro tento klíč. Pokud klíč chybí, Kafka používá strategii lepivého round-robin (dávkové záznamy na stejném oddílu před otočením).

# Creare un topic con 6 partizioni e replication factor 3
# (Kafka 4.0 con KRaft, niente --zookeeper flag)
kafka-topics.sh --create \
  --bootstrap-server kafka1:9092 \
  --topic ordini-effettuati \
  --partitions 6 \
  --replication-factor 3 \
  --config retention.ms=604800000 \
  --config min.insync.replicas=2

# Descrivere il topic per verificare la distribuzione
kafka-topics.sh --describe \
  --bootstrap-server kafka1:9092 \
  --topic ordini-effettuati

Výstup z --describe zobrazuje pro každý oddíl: vedoucího zprostředkovatele, repliky a synchronizované repliky (ISR — In-Sync repliky). ISR jsou repliky, které replikovaly všechny záznamy vůdce: pokud vůdce padne, může být pouze jeden ISR zvolen jako nový vůdce, což zajišťuje, že nedojde ke ztrátě dat.

Producent: Psaní záznamů v Kafkovi

Il výrobce je to složka, která publikuje záznamy o tématech. Záruky dodání určuje konfigurace výrobce. Nejkritičtější vlastnosti jsou:

  • bootstrap.servers: seznam brokerů pro prvotní připojení (ostatní brokery klient objeví automaticky)
  • key.serializer e value.serializer: Jak serializovat klíč a hodnotu (StringSerializer, AvroSerializer atd.)
  • acks: kolik potvrzení odpovědi čekat, než bude zápis považován za úspěšný (0, 1, all)
  • retries: počet pokusů v případě dočasné chyby
  • linger.ms: milisekundy čekání před odesláním dávky (zvyšuje propustnost na úkor latence)
  • batch.size: maximální velikost dávky v bajtech (výchozí: 16 kB)
// Producer Java con configurazione production-ready
import org.apache.kafka.clients.producer.*;
import java.util.Properties;

public class OrdineProducer {

    public static KafkaProducer<String, String> createProducer() {
        Properties props = new Properties();
        props.put("bootstrap.servers", "kafka1:9092,kafka2:9092,kafka3:9092");
        props.put("key.serializer",
            "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer",
            "org.apache.kafka.common.serialization.StringSerializer");

        // Garanzie di consegna: all = acks da tutti le ISR
        props.put("acks", "all");
        // Retry automatico con backoff
        props.put("retries", 3);
        props.put("retry.backoff.ms", 100);
        // Batching per throughput
        props.put("linger.ms", 5);
        props.put("batch.size", 32768);   // 32KB
        // Compressione: riduce I/O di rete del 60-80%
        props.put("compression.type", "snappy");
        // Idempotenza: evita duplicati in caso di retry
        props.put("enable.idempotence", true);

        return new KafkaProducer<>(props);
    }

    public static void inviaOrdine(KafkaProducer<String, String> producer,
                                    String ordineId, String payload) {
        // La chiave (ordineId) determina la partizione target
        ProducerRecord<String, String> record =
            new ProducerRecord<>("ordini-effettuati", ordineId, payload);

        // Invio asincrono con callback
        producer.send(record, (metadata, exception) -> {
            if (exception != null) {
                System.err.println("Errore invio: " + exception.getMessage());
            } else {
                System.out.printf("Record inviato: topic=%s, partizione=%d, offset=%d%n",
                    metadata.topic(), metadata.partition(), metadata.offset());
            }
        });
    }
}

Pozor: acks a propustnost

Zvýšení záruk něco stojí: s acks=all e min.insync.replicas=2, výrobce očekává alespoň 2 odpovědi sepsali záznam před pokračováním. To zvyšuje latenci (obvykle 1-5 ms navíc), ale také zajišťuje, že nedojde ke ztrátě dat pokud makléř okamžitě po potvrzení vypadne. U analytických systémů, které tolerují určité ztráty, acks=1 o acks=0 nabízejí mnohem vyšší propustnost.

Spotřebitel: čtení záznamů od Kafky

Volební smyčka

Spotřebitel Kafka používá šablonu SEM: nepřijímá push zprávy, ale aktivně je požaduje od brokera prostřednictvím volání poll(). Tento design zajišťuje, že spotřebitel není zahlcen objemem zpráv přesahujícím jeho kapacitu zpracování.

// Consumer Java base con gestione degli offset manuale
import org.apache.kafka.clients.consumer.*;
import java.time.Duration;
import java.util.*;

public class OrdineConsumer {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "kafka1:9092,kafka2:9092,kafka3:9092");
        props.put("group.id", "servizio-inventario");
        props.put("key.deserializer",
            "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer",
            "org.apache.kafka.common.serialization.StringDeserializer");

        // Comportamento alla prima lettura (nessun offset salvato per il gruppo)
        // "earliest" = dall'inizio; "latest" = solo nuovi messaggi
        props.put("auto.offset.reset", "earliest");

        // Disabilitiamo il commit automatico per controllo preciso
        props.put("enable.auto.commit", false);

        // Timeout max per il join al consumer group (default: 45s)
        props.put("session.timeout.ms", 30000);

        try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
            consumer.subscribe(List.of("ordini-effettuati"));

            while (true) {
                ConsumerRecords<String, String> records =
                    consumer.poll(Duration.ofMillis(100));

                for (ConsumerRecord<String, String> record : records) {
                    System.out.printf("Offset: %d | Partizione: %d | Chiave: %s%n",
                        record.offset(), record.partition(), record.key());

                    // Elabora il record...
                    elaboraOrdine(record.value());
                }

                // Commit manuale DOPO l'elaborazione
                // Garantisce at-least-once semantics
                if (!records.isEmpty()) {
                    consumer.commitSync();
                }
            }
        }
    }

    private static void elaboraOrdine(String payload) {
        // logica di business...
    }
}

Skupina spotřebitelů: Mechanismus škálování

Il skupina spotřebitelů je to základní mechanismus pro paralelní škálování spotřeby. Sdílení všech spotřebitelů stejný group.id jsou součástí stejné skupiny a rozdělují tématické oddíly. Pravidlo je jednoduché: každý oddíl může být přiřazen pouze jednomu spotřebiteli na skupinu najednou.

To znamená, že maximální počet paralelních spotřebitelů ve skupině se rovná počtu oddílů. Pokud máte 6 oddílů a spustíte 6 spotřebitelů ve stejné skupině, každý dostane přesně 1 oddíl. Pokud spustíte 7. spotřebitele, zůstane nečinný v pohotovostním režimu (užitečné pro rychlé převzetí služeb při selhání).

Skupina spotřebitelů: Scénáře škálování

  • 1 spotřebitel, 6 přepážek → spotřebitel zpracovává vše, žádný paralelismus
  • 3 spotřebiče, 6 oddílů → každý spotřebitel spravuje 2 oddíly paralelně
  • 6 spotřebičů, 6 oddílů → maximální paralelismus, 1 oddíl na spotřebitele
  • 9 spotřebičů, 6 oddílů → 6 aktivních, 3 v pohotovostním režimu pro převzetí služeb při selhání
  • Dvě odlišné skupiny, stejné téma → každá skupina přijímá VŠECHNY zprávy nezávisle

Offsety: Mechanismus sledování polohy

L'offset je celé číslo, které jednoznačně identifikuje umístění záznamu v oddílu. Zprostředkovatel přiřazuje offset postupně každému zapsanému záznamu: první záznam má offset 0, druhý 1 a tak dále.

Skupina spotřebitelů skladuje své vlastní zavázaný offset — tj. offset posledního úspěšně zpracovaného záznamu — ve speciálním interním kafkovském tématu tzv __consumer_offsets. Toto je výchozí bod v případě restartu nebo převzetí služeb při selhání spotřebitele.

Pochopení rozdílu mezi těmito posuny je rozhodující pro zpracování chyb:

  • Log End Offset (LEO): offset dalšího záznamu, který bude zapsán do protokolu (pozice hlavy)
  • Vysoký vodoznak (HW): offset posledního záznamu replikovaného napříč všemi ISR ​​(zákazník vidí pouze záznamy ≤ HW)
  • Aktuální offset: offset dalšího záznamu, který si spotřebitel přečte v příštím volání poll().
  • Committed Offset: posun uložený v tématu __consumer_offsets (ze kterého se restartuje po havárii)
  • Spotřebitelské zpoždění: Rozdíl mezi LEO a Committed Offset udává, kolik záznamů musí spotřebitel ještě zpracovat
# Controllare il consumer lag di un gruppo
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --describe \
  --group servizio-inventario

# Output tipico:
# GROUP              TOPIC                PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
# servizio-inventario ordini-effettuati    0          1250            1280            30
# servizio-inventario ordini-effettuati    1          890             890             0
# servizio-inventario ordini-effettuati    2          2100            2105            5

# Resettare gli offset al principio (per reprocessing)
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --group servizio-inventario \
  --topic ordini-effettuati \
  --reset-offsets --to-earliest \
  --execute

Replikace: Trvanlivost a odolnost proti chybám

Každý oddíl má a vůdce a nula nebo více následovníci (repliky). Výrobci a spotřebitelé komunikují vždy s vedoucím. Následovníci replikují data z lídra asynchronním, ale obecně rychlým způsobem.

Soubor následovníků, kteří jsou „dostatečně aktualizováni“, aby se stali vůdci, tvoříISR (In-Sync Replicas). Následovník je odstraněn z ISR, pokud zaostává o více než replica.lag.time.max.ms milisekundy (výchozí: 30 s). Když vůdce padne, Kafka kontrolor zvolí následovníka s nejvyšším posunem mezi ISR ​​jako nového vůdce.

Kombinace replication.factor e min.insync.replicas definuje kompromis mezi trvanlivostí a dostupností:

# Configurazione consigliata per produzione
# replication.factor=3 significa: 1 leader + 2 follower

# topic-level overrides
kafka-topics.sh --alter \
  --bootstrap-server kafka1:9092 \
  --topic ordini-effettuati \
  --config min.insync.replicas=2

# Con questa configurazione:
# - acks=all: producer aspetta conferma da leader + 1 follower minimo
# - Se 2 broker su 3 sono down: il cluster rifiuta scritture (ma no data loss)
# - Se solo 1 broker è down: il cluster continua normalmente

# broker-level defaults in server.properties
default.replication.factor=3
min.insync.replicas=2
offsets.topic.replication.factor=3
transaction.state.log.replication.factor=3

Zásady uchovávání: Jak dlouho data zůstávají

Záznamy v Kafka jsou odstraněny podle zásad, které lze nakonfigurovat podle tématu. Existují dva hlavní režimy:

  • Uchovávání na základě času (retention.ms): Záznamy jsou smazány po určité době z jejich časového razítka. Výchozí: 604800000 ms = 7 dní. Pro kritická témata, jako jsou protokoly auditu, jsou nastaveny mnohem vyšší hodnoty (roky).
  • Retence na základě velikosti (retention.bytes): protokol na oddíl nepřesahuje určitou velikost. Když velikost překročí limit, budou starší segmenty odstraněny.
  • Zhutňování kulatiny (cleanup.policy=compact): místo mazání podle času/velikost Kafka ponechává pouze poslední záznam pro každý klíč. Ideální pro státní témata (jako jsou databázové tabulky replikované přes CDC).
# Configurare retention per diversi use case

# Topic eventi real-time: retention breve, alta velocità
kafka-topics.sh --create \
  --bootstrap-server kafka1:9092 \
  --topic click-stream \
  --partitions 12 \
  --replication-factor 3 \
  --config retention.ms=3600000 \   # 1 ora
  --config retention.bytes=1073741824  # 1GB per partizione

# Topic log audit: retention lunga per compliance
kafka-topics.sh --create \
  --bootstrap-server kafka1:9092 \
  --topic audit-log \
  --partitions 3 \
  --replication-factor 3 \
  --config retention.ms=31536000000 \  # 1 anno
  --config compression.type=gzip

# Topic di stato con log compaction (es. profili utente)
kafka-topics.sh --create \
  --bootstrap-server kafka1:9092 \
  --topic profili-utente \
  --partitions 6 \
  --replication-factor 3 \
  --config cleanup.policy=compact \
  --config min.cleanable.dirty.ratio=0.5

Consumer Python: Praktický příklad

Ekosystém Kafka podporuje mnoho jazyků. Zde je příklad Pythonu s použitím knihovny confluent-kafka (oficiální vazba Confluent založená na librdkafka, mnohem výkonnější než kafka-python):

# pip install confluent-kafka

from confluent_kafka import Consumer, KafkaError, KafkaException
import json
import signal
import sys

TOPIC = "ordini-effettuati"
GROUP_ID = "servizio-analytics-py"

config = {
    "bootstrap.servers": "kafka1:9092,kafka2:9092",
    "group.id": GROUP_ID,
    "auto.offset.reset": "earliest",
    "enable.auto.commit": False,
    "session.timeout.ms": 30000,
    "max.poll.interval.ms": 300000,  # 5 minuti per elaborazioni lente
}

consumer = Consumer(config)
running = True

def graceful_shutdown(signum, frame):
    global running
    running = False

signal.signal(signal.SIGINT, graceful_shutdown)
signal.signal(signal.SIGTERM, graceful_shutdown)

try:
    consumer.subscribe([TOPIC])
    print(f"Consumer avviato, gruppo: {GROUP_ID}")

    while running:
        msg = consumer.poll(timeout=1.0)

        if msg is None:
            continue

        if msg.error():
            if msg.error().code() == KafkaError._PARTITION_EOF:
                # Raggiunta la fine della partizione, aspetta nuovi messaggi
                print(f"Raggiunto EOF: {msg.topic()} [{msg.partition()}] offset {msg.offset()}")
            elif msg.error():
                raise KafkaException(msg.error())
        else:
            ordine = json.loads(msg.value().decode("utf-8"))
            print(f"Ricevuto ordine {ordine['id']} da partizione {msg.partition()}")

            # Elabora l'ordine...
            elabora_ordine(ordine)

            # Commit manuale dopo elaborazione riuscita
            consumer.commit(asynchronous=False)

finally:
    consumer.close()
    print("Consumer chiuso correttamente")

def elabora_ordine(ordine):
    # Logica di business...
    pass

Doporučená architektura: Kolik oddílů?

Jedna z nejčastějších otázek pro ty, kteří začínají s Kafkou, je: kolik oddílů vytvořit pro téma? Odpověď závisí na několika faktorech:

  • Maximální paralelnost skupiny spotřebitelů: počet oddílů je maximální počet paralelních spotřebičů. Odhadněte počet spotřebitelů, které očekáváte na vrcholu.
  • Cílová propustnost: Každý oddíl obvykle zvládne zápis 10-50 MB/s (v závislosti na disku). Vydělte celkovou propustnost tímto číslem, abyste získali minimální počet potřebných oddílů.
  • Řazení: pokud potřebujete zaručit objednávku na určitý klíč (např. všechny akce stejného zákazníka), tento klient vždy skončí na stejném oddílu. Více oddílů = lepší rozložení zátěže pro různé klíče.
  • Paměť nad hlavou: Každý oddíl vyžaduje paměť v zprostředkovateli (~1-2 MB režie). S celkovým počtem 100 000 oddílů, začíná to vybírat daň.

Praktické pravidlo pro oddíly

Přibližný vzorec: max(throughput_MB_s / 10, consumer_max_paralleli). Pro většinu aplikací, 6, 12 nebo 24 oddílů jsou rozumné hodnoty. Kafka umožňuje rozšiřovat oddíly později, ale abych je nesnižoval: Plánujte s malou rezervou.

Zhutňování protokolů: Případ použití pro státní témata

La hutnění kulatiny je pokročilá funkce Kafky, která zcela mění sémantiku uchovávání: místo mazání záznamů podle času nebo velikosti uchovává Kafka pouze záznamyposlední záznam pro každý klíč. Všechny starší záznamy se stejným klíčem jsou během procesu komprimace vymazány.

Díky tomu jsou kompaktní témata ideální pro reprezentaci aktuální stav entity: uživatelské profily, aktuální ceny, konfigurace systému, inventář. Spotřebitel připojující se k tématu poprvé zhutněný dokáže rekonstruovat kompletní stav načtením všech přítomných záznamů (jeden na klíč), aniž byste museli číst celou historii událostí.

Rekord s hodnotou null („záznam náhrobku“) je způsob, jak odstranit klíč z tématu zhutněno: po zhutnění zmizí z klády i samotný klíč.

# Creare un topic con log compaction
kafka-topics.sh --create \
  --bootstrap-server kafka1:9092 \
  --topic profili-utente \
  --partitions 6 \
  --replication-factor 3 \
  --config cleanup.policy=compact \
  --config min.cleanable.dirty.ratio=0.5 \
  --config segment.ms=86400000 \
  --config delete.retention.ms=86400000

# cleanup.policy=compact: abilita compaction
# min.cleanable.dirty.ratio=0.5: compatta quando >50% del log e' "dirty"
# segment.ms=86400000: crea un nuovo segmento ogni 24h
# delete.retention.ms: quanto tenere i tombstone record prima di eliminarli

# Inviare un aggiornamento profilo (chiave = userId)
kafka-console-producer.sh \
  --bootstrap-server kafka1:9092 \
  --topic profili-utente \
  --property parse.key=true \
  --property key.separator=:
# Digita: user123:{"nome":"Mario","email":"mario@example.com","eta":30}
# Digita: user456:{"nome":"Anna","email":"anna@example.com","eta":25}
# Digita: user123:{"nome":"Mario","email":"mario.rossi@example.com","eta":31}
# Dopo compaction, nel topic rimane solo l'ultima riga per user123

Téma Kafka internal: __consumer_offsets a __transaction_state

Kafka interně využívá speciální témata k řízení svého stavu. Jejich znalost pomáhá pochopit, jak fungují systému a k odstraňování problémů:

  • __consumer_offsets: ukládá potvrzené ofsety každé skupiny spotřebitelů. Ve výchozím nastavení má 50 oddílů (offsets.topic.num.partitions). Je přiřazena skupina spotřebitelů do oddílu přes hash group.id. Pokud má toto téma problémy s replikací, skupiny spotřebitelů nedaří se jim provést kompenzace.
  • __transaction_state: spravuje stav probíhajících transakcí. Používá transakční API Kafka k zaručení sémantiky přesně jednou. Ve výchozím nastavení má 50 oddílů.
  • @metadata (Pouze KRaft): Protokol metadat řadiče kvora. Obsahuje všechna metadata clusteru (témata, oddíly, makléři, ACL, konfigurace). Přístupné pouze interně ovladačům.
# Ispezionare il topic __consumer_offsets (advanced troubleshooting)
# ATTENZIONE: operazione read-only, non modificare mai questi topic

kafka-console-consumer.sh \
  --bootstrap-server kafka1:9092 \
  --topic __consumer_offsets \
  --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" \
  --from-beginning \
  --max-messages 20

# Output esempio:
# [servizio-inventario,ordini-effettuati,0]::OffsetAndMetadata(offset=1250, ...)
# [servizio-inventario,ordini-effettuati,1]::OffsetAndMetadata(offset=890, ...)

# Elencare tutti i consumer group attivi
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --list

# Dettaglio di un gruppo specifico
kafka-consumer-groups.sh \
  --bootstrap-server kafka1:9092 \
  --group servizio-inventario \
  --describe \
  --state  # include stato del gruppo (Stable, Rebalancing, Empty, Dead)

Záhlaví zprávy a časové razítko

Každá Kafkova deska má přesnou strukturu:

  • Klíč (volitelné): určuje cílový oddíl, serializovaný v bajtech
  • Hodnota: užitečné zatížení zprávy, serializované v bajtech
  • Časové razítko: čas vytvoření na straně výrobce (CreateTime) nebo příjem na straně zprostředkovatele (LogAppendTime), konfigurovatelné
  • Záhlaví: páry klíč–hodnota pro metadata (ID korelace, typ události, verze schématu atd.)
  • Partition + Offset: přiděleno makléřem v době psaní
// Aggiungere headers a un ProducerRecord Java
ProducerRecord<String, String> record = new ProducerRecord<>(
    "ordini-effettuati",
    ordineId,
    payload
);

// Headers per tracciabilità e versioning
record.headers()
    .add("correlation-id", UUID.randomUUID().toString().getBytes())
    .add("schema-version", "2".getBytes())
    .add("source-service", "checkout-service".getBytes())
    .add("event-type", "OrdineCreato".getBytes());

producer.send(record);

Docker Compose: Rychlý start pro místní rozvoj

Chcete-li začít experimentovat s Kafkou lokálně, aniž byste museli řešit složité konfigurace, nejrychlejší způsob je použít Docker Compose s oficiálním obrázkem Apache Kafka 4.0:

# docker-compose.yml minimale per sviluppo locale (single-node KRaft)
version: "3.9"
services:
  kafka:
    image: apache/kafka:4.0.0
    container_name: kafka-local
    ports:
      - "9092:9092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: "broker,controller"
      KAFKA_LISTENERS: "PLAINTEXT://kafka-local:9092,CONTROLLER://kafka-local:9093"
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092"
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
      KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
      KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka-local:9093"
      KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"
      CLUSTER_ID: "local-dev-cluster-id-001"
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"

# Avvio:
# docker-compose up -d
#
# Verifica:
# docker exec kafka-local kafka-topics.sh --bootstrap-server localhost:9092 --list

Shrnutí: Základní pojmy

Po prozkoumání vnitřní struktury Kafky je zde shrnutí klíčových pojmů, které je třeba si zapamatovat:

  • Makléř: uzel clusteru, spravuje diskové protokoly a replikaci
  • Témata: logická kategorie záznamů, rozdělená do oddílů
  • Rozdělit: seřazený protokol pouze pro připojení, jednotka paralelismu; objednání zaručeno pouze uvnitř
  • Offset: progresivní pozice každého záznamu v oddílu
  • Skupina spotřebitelů: škálovací mechanismus; každý oddíl je přiřazen pouze jednomu spotřebiteli na skupinu
  • ISR: sada aktualizovaných replik, ze kterých je v případě poruchy zvolen nový vůdce
  • Spotřebitelské zpoždění: indikátor kritického zdraví, rozdíl mezi LEO a potvrzeným offsetem
  • Udržení: Záznamy zůstávají konfigurovatelné, po konzumaci se nesmažou

Další kroky v sérii

Nyní, když máte pevný základ, další články série se ponoří do pokročilejších aspektů:

  • Článek 2 – KRaft v Kafka 4.0: jak nový ovladač funguje bez ZooKeeper, proces migrace z Kafka 3.xa provozní výhody ve výrobě.
  • Článek 3 – Pokročilý výrobce a spotřebitel: podrobná konfigurace acks, retries, max.in.flight.requests a idempotentní výrobce garantuje přesně jednou na úrovni výrobce.
  • Článek 4 – Sémantika přesně jednou: Kafka transakce pro atomic píše na různá témata, koordinátora transakcí a dopady na propustnost.

Propojení s ostatními sériemi

  • Pozorovatelnost a OpenTelemetry: Jak instrumentovat aplikaci Kafka pomocí OpenTelemetry sledovat šíření událostí mezi výrobci a spotřebiteli.
  • Platform Engineering: Kafka jako základní součást interní vývojářské platformy pro událostmi řízenou komunikaci mezi týmy.
  • PostgreSQL AI: Vzor CDC (Change Data Capture) s Debezium pro synchronizaci PostgreSQL ke Kafkovi v reálném čase, téma článku 7 této série.