De ce Kafka este diferit de o Coda tradițională

Când proiectați un sistem distribuit care trebuie să gestioneze fluxurile de evenimente în timp real, prima tentație este să folosiți o coadă de mesaje clasică. precum RabbitMQ sau ActiveMQ. Aceste soluții funcționează bine pentru scenarii simple, dar au o limitare structurală cheie: odată consumatorul consumat mesajul, mesajul este șters. Nu există posibilitatea de a o reciti, de a avea mai mulți consumatori independenți care o prelucrează diferit, sau pentru a relua întreaga istorie a evenimentelor.

Apache Kafka s-a născut în 2011 în LinkedIn cu o filozofie radical diferită: mesajele (numite înregistra) sunt scrise în a jurnal numai pentru adăugare și rămâne acolo pentru o perioadă configurabilă (implicit: 7 zile). Diferiți consumatori pot citi uneori aceleași înregistrări diferite, fiecare ținând evidența poziției sale prin intermediuloffset. Acest model îl transformă pe Kafka în mult mai mult decât o coadă: devine cel sursa adevarului pentru întregul istoric al evenimentelor din sistemul dumneavoastră.

Kafka în 2026: numere cheie

  • Folosit de peste 80% dintre companiile Fortune 500 pentru cazuri de utilizare în flux
  • Kafka 4.0 (martie 2025) a eliminat definitiv ZooKeeper, trecând la KRaft
  • Debit teoretic: Peste 1 milion de mesaje/secundă pentru brokeri (hardware de marfă)
  • Confluent Cloud: Kafka gestionat disponibil pe AWS, GCP, Azure cu latență < 10 ms p99
  • Ecosistem: peste 200 de conectori prin Kafka Connect, Kafka Streams, integrare Apache Flink

Modelul fundamental: broker, subiect și partiție

Broker: Nodul Cluster

Un broker este pur și simplu un server Kafka. Un cluster Kafka este compus din unul sau mai mulți brokeri, fiecare identificat prin a broker.id unic. În producție, se folosesc de obicei 3, 6 sau 9 brokeri pentru a garanta toleranța la erori. Brokerii se ocupă de scriere și citirea înregistrărilor, menținerea jurnalelor pe disc și replicarea între noduri.

Cu Kafka 4.0 și noul mod KRaft, unul sau mai mulți brokeri își asumă și rolul de controlor, gestionând metadatele clusterului (cine este liderul cărei partiții, ce brokeri sunt activi etc.) printr-un jurnal intern de consens Raft. Nu mai este nevoie a unui ansamblu ZooKeeper separat.

Subiect: Categoria logică a înregistrărilor

Un subiect este numele logic sub care producătorii publică înregistrările și consumatorii le citesc. Vă puteți gândi la el ca pe un canal tematic: ordini-effettuati, pagamenti-confermati, eventi-utente. Fiecare subiect are propria configurație pentru reținere, numărul de partiții, factorul de replicare și politicile de compactare.

Subiectele sunt compartimentat: fiecare subiect este împărțit în N partiții fizice, distribuite între brokeri. Este această distribuție ceea ce face ca Kafka să fie scalabil pe orizontală atât pentru scris, cât și pentru citit.

Despărțire: Unitatea paralelismului și ordonării

Una compartimentare este un jurnal ordonat și imuabil numai pentru adăugare. Fiecare înregistrare scrisă pe o partiție primește un offset crescător monoton (0, 1, 2, ...). Sortarea este garantată in interiorul partitiei, nu între diferite partiții.

Distribuția înregistrărilor între partiții este determinată de cheie de partiție: Dacă producătorul specifică o cheie, înregistrarea merge întotdeauna la aceeași partiție (hash al modulului cheii, numărul de partiții), garantând sortarea pentru acea cheie. Dacă cheia lipsește, Kafka folosește o strategie round-robin (înregistrări de lot pe aceeași partiție înainte de rotație).

# 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

Ieșirea de --describe afișează pentru fiecare partiție: brokerul lider, replicile și replicile în sincronizare (ISR — Replici în sincronizare). ISR-urile sunt replicile care au replicat toate înregistrările liderului: dacă liderul cade, un singur ISR poate fi ales ca noul lider, asigurând nicio pierdere de date.

Producătorul: Scrierea de discuri în Kafka

Il producător este componenta care publică înregistrări pe subiecte. Configurația producătorului determină garanțiile de livrare. Cele mai critice proprietăți sunt:

  • bootstrap.servers: lista de brokeri pentru conexiunea initiala (clientul descopera automat ceilalti brokeri)
  • key.serializer e value.serializer: Cum să serializeze cheia și valoarea (StringSerializer, AvroSerializer etc.)
  • acks: câte confirmări de răspuns să așteptați înainte de a considera scrierea reușită (0, 1, all)
  • retries: numărul de încercări în caz de eroare temporară
  • linger.ms: milisecunde de așteptat înainte de a trimite un lot (mărește debitul în detrimentul latenței)
  • batch.size: dimensiunea maximă a lotului în octeți (implicit: 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());
            }
        });
    }
}

Atenție: ack și Throughput

Creșterea garanțiilor are un cost: cu acks=all e min.insync.replicas=2, producătorul așteaptă cel puțin 2 răspunsuri au scris dosarul înainte de a continua. Acest lucru adaugă latență (de obicei 1-5 ms suplimentar), dar asigură și nicio pierdere de date dacă un broker scade imediat după confirmare. Pentru sistemele analitice care tolerează anumite pierderi, acks=1 o acks=0 oferă un randament mult mai mare.

Consumatorul: Citirea înregistrărilor de la Kafka

Bucla de sondare

Consumatorul Kafka folosește un șablon Trage: nu primește mesaje push, dar le solicită în mod activ de la broker prin apeluri poll(). Acest design asigură că consumatorul nu este copleșit de un volum de mesaje peste capacitatea sa de prelucrare.

// 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...
    }
}

Grupul de consumatori: Mecanismul de scalare

Il grup de consumatori este mecanismul fundamental de scalare a consumului în paralel. Toți consumatorii împărtășesc la fel group.id fac parte din același grup și împart partițiile de subiecte. Regula este simplă: fiecare partiție poate fi atribuită unui singur consumator per grup la un moment dat.

Aceasta înseamnă că numărul maxim de consumatori paraleli dintr-un grup este egal cu numărul de partiții. Dacă aveți 6 partiții și porniți 6 consumatori în același grup, fiecare primește exact 1 partiție. Dacă porniți un al șaptelea consumator, acesta va rămâne inactiv în standby (util pentru failover rapid).

Grup de consumatori: scenarii de scalare

  • 1 consumator, 6 partitii → un consumator procesează totul, fără paralelism
  • 3 consumatori, 6 partitii → fiecare consumator gestioneaza 2 partitii in paralel
  • 6 consumatori, 6 partitii → paralelism maxim, 1 partiție per consumator
  • 9 consumatori, 6 partiții → 6 active, 3 în standby pentru failover
  • Două grupuri distincte, același subiect → fiecare grup primește TOATE mesajele în mod independent

Compensații: Mecanismul de urmărire a poziției

L'offset este un număr întreg care identifică în mod unic locația unei înregistrări într-o partiție. Brokerul atribuie offset-ul secvenţial fiecărei înregistrări scrise: prima înregistrare are un offset de 0, a doua 1 şi aşa mai departe.

Grupul de consumatori își păstrează propriile sale compensare comisă — adică decalajul ultimei înregistrări procesate cu succes — într-un subiect intern special Kafka numit __consumer_offsets. Acesta este punctul de pornire în cazul unei reporniri sau failoverul consumatorului.

Înțelegerea diferenței dintre aceste decalaje este critică pentru tratarea erorilor:

  • Compensarea sfârșitului jurnalului (LEO): compensarea următoarei înregistrări care va fi scrisă în jurnal (poziția capului)
  • Filigran ridicat (HW): compensarea ultimei înregistrări replicate în toate ISR-urile (consumatorul vede doar înregistrările ≤ HW)
  • Offset curent: compensarea următoarei înregistrări pe care consumatorul o va citi în următorul apel poll().
  • Compensare comisă: offset-ul salvat în subiect __consumer_offsets (de la care să reporniți după un accident)
  • Întârzierea consumatorului: Diferența dintre LEO și Committed Offset, indică câte înregistrări trebuie să proceseze consumatorul
# 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

Replicare: durabilitate și toleranță la erori

Fiecare partiție are un lider și zero sau mai mult urmași (replici). Producătorii și consumatorii comunică mereu cu liderul. Adepții reproduce datele de la lider într-un mod asincron, dar în general rapid.

Setul de adepți care sunt „suficient de actualizați” pentru a deveni lideri formeazăISR (replici în sincronizare). Un adept este eliminat din ISR dacă rămâne în urmă cu mai mult de replica.lag.time.max.ms milisecunde (implicit: 30s). Când liderul cade, controlorul Kafka alege adeptul cu cel mai mare decalaj dintre ISR ca noul lider.

Combinația de replication.factor e min.insync.replicas definește compromisul dintre durabilitate și disponibilitate:

# 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

Politica de păstrare: Cât timp rămân datele

Înregistrările din Kafka sunt eliminate conform politicilor care pot fi configurate pe subiect. Există două moduri principale:

  • Retentie bazata pe timp (retention.ms): Înregistrările sunt șterse după o anumită perioadă din marcajul de timp. Implicit: 604800000ms = 7 zile. Pentru subiectele critice, cum ar fi jurnalele de audit, sunt setate valori mult mai mari (ani).
  • Retenție bazată pe dimensiune (retention.bytes): jurnalul per partiție nu depășește o anumită dimensiune. Când dimensiunea depășește limita, segmentele mai vechi sunt șterse.
  • Compactarea buștenilor (cleanup.policy=compact): în loc să șterge după timp/dimensiune, Kafka păstrează doar ultima înregistrare pentru fiecare cheie. Ideal pentru subiecte de stat (cum ar fi tabelele de baze de date replicate prin 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: Un exemplu practic

Ecosistemul Kafka acceptă multe limbi. Iată un exemplu Python folosind biblioteca confluent-kafka (legarea oficială Confluent bazată pe librdkafka, mult mai performantă decât 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

Arhitectură recomandată: Câte partiții?

Una dintre cele mai frecvente întrebări pentru cei care încep cu Kafka este: câte partiții să creați pentru un subiect? Răspunsul depinde de mai mulți factori:

  • Paralelismul maxim al grupului de consumatori: numărul de partiții este numărul maxim de consumatori paraleli. Estimați numărul de consumatori pe care vă așteptați să-i aveți la vârf.
  • Debitul țintă: Fiecare partiție poate gestiona de obicei 10-50 MB/s scriere (depinde de disc). Împărțiți debitul total la această cifră pentru a obține numărul minim de partiții necesare.
  • Triere: dacă trebuie să garantați comanda pentru o anumită cheie (de exemplu, toate evenimentele aceluiași client), acel client va ajunge întotdeauna pe aceeași partiție. Mai multe partiții = o distribuție mai bună a încărcării pentru diferite chei.
  • Memorie deasupra capului: Fiecare partiție necesită memorie în broker (aproximativ 1-2 MB). Cu 100K partiții totale, începe să ia o taxă.

Regulă practică pentru partiții

O formulă aproximativă: max(throughput_MB_s / 10, consumer_max_paralleli). Pentru majoritatea aplicațiilor, 6, 12 sau 24 de partiții sunt valori rezonabile. Kafka vă permite să creșteți partițiile mai târziu, dar să nu le diminueze: Plan cu o mică marjă.

Compactarea jurnalului: cazul de utilizare pentru subiectele de stat

La compactare bușteni este o caracteristică avansată a lui Kafka care schimbă complet semantica reținere: în loc să șterge înregistrările după timp sau dimensiune, Kafka păstrează doar fișierulultima înregistrare pentru fiecare cheie. Toate înregistrările mai vechi cu aceeași cheie sunt șterse în timpul procesului de compactare.

Acest lucru face ca subiectele compacte să fie ideale pentru reprezentarea starea actuală de entitate: profiluri de utilizator, prețuri curente, configurații de sistem, inventar. Un consumator care se conectează la un subiect compactat pentru prima dată poate reconstrui starea completă citind toate înregistrările prezente (una pe tastă), fără a fi nevoie să citiți întreaga istorie a evenimentelor.

Un record cu valoare null („înregistrarea piatră funerară”) este modalitatea de a șterge o cheie dintr-un subiect compactat: după compactare, cheia însăși dispare din buștean.

# 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

Subiectul intern Kafka: __consumer_offsets și __transaction_state

Kafka folosește în interior subiecte speciale pentru a-și gestiona starea. Cunoașterea lor vă ajută să înțelegeți cum funcționează a sistemului și pentru a depana:

  • __consumer_offsets: stochează compensațiile angajate ale fiecărui grup de consumatori. Are 50 de partiții în mod implicit (offsets.topic.num.partitions). Se desemnează grupul de consumatori la o partiție prin hash a grupului.id. Dacă acest subiect are probleme de replicare, grupurile de consumatori nu reușesc să comită compensații.
  • __transaction_state: gestionează starea tranzacțiilor în curs. Folosit de API-ul tranzacțional Kafka pentru a garanta o semantică exactă. Are 50 de partiții în mod implicit.
  • @metadata (Numai KRaft): jurnalul de metadate al controlerului de cvorum. Conține toate metadatele clusterului (subiecte, partiții, brokeri, ACL-uri, configurații). Accesibil doar intern pentru controlere.
# 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)

Antetul mesajului și marcajul de timp

Fiecare înregistrare Kafka are o structură precisă:

  • Cheie (opțional): determină partiția țintă, serializată în octeți
  • Valoare: sarcina utilă a mesajului, serializat în octeți
  • Marca temporală: timpul de creare din partea producătorului (CreateTime) sau ingestie de partea brokerului (LogAppendTime), configurabil
  • Anteturi: perechi cheie-valoare pentru metadate (ID de corelare, tipul evenimentului, versiunea schemei etc.)
  • Partiție + Offset: atribuit de broker la momentul scrierii
// 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: pornire rapidă pentru dezvoltare locală

Pentru a începe să experimentați cu Kafka la nivel local, fără a vă ocupa de configurații complexe, cel mai rapid mod este să utilizați Docker Compose cu imaginea oficială 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

Rezumat: Conceptele fundamentale

După ce am explorat structura internă a lui Kafka, iată un rezumat al conceptelor cheie de memorat:

  • Broker: nod cluster, gestionează jurnalele discului și replicarea
  • Subiecte: categorie logică de înregistrări, împărțite în partiții
  • Partiție: jurnal sortat numai pentru adăugare, unitate de paralelism; comanda garantata doar in interior
  • Offset: poziția progresivă a fiecărei înregistrări într-o partiție
  • Grupul de consumatori: mecanism de scalare; fiecare partiție alocată unui singur consumator per grup
  • ISR: set de replici actualizate, din care este ales noul lider în caz de greșeală
  • Întârzierea consumatorului: indicator critic de sănătate, diferența dintre LEO și compensarea comisă
  • Retenţie: Înregistrările rămân configurabile, nu sunt șterse după consum

Următorii pași din serie

Acum că aveți o bază solidă, următoarele articole din serie se aprofundează în aspecte mai avansate:

  • Articolul 2 – KRaft în Kafka 4.0: cum funcționează noul controler fără ZooKeeper, procesul de migrare de la Kafka 3.x și beneficiile operaționale în producție.
  • Articolul 3 – Producător și Consumator avansat: configurația detaliată a acks, retries, max.in.flight.requests iar producătorul idempotent pentru garanții exact-o dată la nivel de producător.
  • Articolul 4 – Semantica Exact O dată: tranzacții Kafka pentru scrieri atomice pe mai multe subiecte, coordonatorul tranzacției și implicațiile asupra debitului.

Legătură cu alte serii

  • Observabilitate și OpenTelemetry: Cum se instrumentează o aplicație Kafka cu OpenTelemetry pentru a urmări propagarea evenimentelor între producători și consumatori.
  • Ingineria platformei: Kafka ca componentă fundamentală a unei platforme pentru dezvoltatori interni pentru comunicarea bazată pe evenimente între echipe.
  • AI PostgreSQL: model CDC (Change Data Capture) cu Debezium pentru a sincroniza PostgreSQL lui Kafka în timp real, subiectul articolului 7 din această serie.