Die Entmystifizierung von Kafkas Exactly-Once Semantics: Ein umfassender Leitfaden
Verstehen Sie Kafkas Exactly-Once-Semantik mit idempotenten Produzenten, Transaktionen, read_committed-Verbrauchern und Offset-Commits.
Kafkas Exactly-Once-Semantik entmystifizieren: Ein umfassender Leitfaden
Kafkas Exactly-Once-Semantik kann eine Stream-Verarbeitungspipeline vor doppelten Ausgabedatensätzen schützen, wenn Produzenten wiederholen, Broker ausfallen oder eine Anwendung neu startet. Die Garantie ist leistungsstark, aber enger als der Begriff klingt: Kafka kann Kafka-Schreibvorgänge und konsumierte Offsets transaktional machen. Es kann nicht automatisch Ihre externe Datenbank, Ihr Zahlungsgateway oder Ihre HTTP-API exactly-once machen.
Verwenden Sie Exactly-Once-Semantik, wenn doppelte Ausgaben teuer oder schwer zu bereinigen wären, wie z. B. Bestandsanpassungen, Kontostandsereignisse oder abgeleitete Zustands-Themen, die von anderen Diensten konsumiert werden.
Zustellgarantien in einfachem Deutsch
Kafka-Anwendungen sprechen normalerweise über drei Liefermodelle.
- At-most-once: Ihre App kann Datensätze verlieren, sollte aber denselben Datensatz nicht zweimal verarbeiten. Dies kann passieren, wenn Offsets vor Abschluss der Verarbeitung committet werden.
- At-least-once: Ihre App sollte keine Datensätze verlieren, kann aber einen Datensatz nach einem Wiederholungsversuch oder Neustart mehr als einmal verarbeiten.
- Exactly-once: Eine Kafka-Lese-Verarbeite-Schreibe-Schleife committet ihre Ausgabedatensätze und ihre konsumierten Offsets als eine Transaktion.
Der letzte Punkt ist der Schlüssel. Exactly-Once-Semantik ist am stärksten, wenn die Anwendung von Kafka liest, Ergebnisse zurück an Kafka schreibt und Offsets innerhalb derselben Transaktion committet.
Idempotente Produzenten
Ein idempotenter Produzent verhindert doppelte Schreibvorgänge, die durch Produzentenwiederholungen verursacht werden. Kafka weist dem Produzenten eine ID zu und verfolgt Sequenznummern für jeden Produzenten und jede Partition. Wenn der Broker bereits einen Batch akzeptiert hat und dann den Wiederholungsversuch erhält, kann er das Duplikat ablehnen, anstatt es erneut anzuhängen.
Für aktuelle Kafka-Clients ist Idempotenz standardmäßig aktiviert, wenn Sie keine widersprüchlichen Produzenteneinstellungen konfigurieren. Sie können es dennoch explizit festlegen:
enable.idempotence=true
acks=all
acks=all bedeutet, dass der Leader auf alle synchronen Replicas wartet, bevor er den Schreibvorgang bestätigt. Idempotenz hängt auch von kompatiblen Wiederholungs- und In-Flight-Request-Einstellungen ab. Vermeiden Sie daher, die Zuverlässigkeitseinstellungen des Produzenten zu überschreiben, es sei denn, Sie kennen die Auswirkungen in Ihrer Client-Version.
Idempotenz schützt vor Produzentenwiederholungen, macht aber einen vollständigen Verarbeitungsworkflow nicht atomar. Wenn Ihre App von einem Thema konsumiert und in ein anderes produziert, benötigen Sie Transaktionen, um die Ausgabe und den Offset-Commit zusammenzubinden.
Kafka-Transaktionen
Transaktionen ermöglichen es einem Produzenten, mehrere Schreibvorgänge in einer atomaren Einheit zu gruppieren. Der Produzent benötigt eine stabile transactional.id.
transactional.id=inventory-adjuster-0
enable.idempotence=true
acks=all
Ein typischer Transaktionsablauf ist:
- Initialisieren Sie Transaktionen beim Start der Anwendung.
- Beginnen Sie eine Transaktion.
- Konsumieren Sie Datensätze vom Eingangsthema.
- Produzieren Sie Ausgabedatensätze.
- Senden Sie die konsumierten Offsets an die Transaktion.
- Committen Sie die Transaktion oder brechen Sie sie bei Fehler ab.
Wenn der Prozess vor dem Commit abstürzt, macht Kafka die nicht committete Ausgabe für read_committed-Verbraucher nicht sichtbar. Beim Neustart kann die Anwendung dieselben Eingabedatensätze erneut lesen und ein committetes Ergebnis produzieren.
Verbrauchereinstellungen, die wichtig sind
Verbraucher, die transaktionale Ausgaben lesen, sollten Folgendes verwenden:
isolation.level=read_committed
enable.auto.commit=false
read_committed verbirgt Datensätze aus abgebrochenen Transaktionen. enable.auto.commit=false verhindert, dass der Verbraucher Offsets außerhalb der Transaktion committet.
Der Eigenschaftsname ist wichtig. Die Kafka-Verbrauchereinstellung ist enable.auto.commit, nicht auto.commit.enable.
Für eine manuelle Verbraucher-Produzenten-App muss der Offset-Commit Teil der Produzententransaktion sein. Im Java-Client bedeutet dies die Verwendung der transaktionalen Produzenten-APIs, einschließlich des Sendens von Offsets an die Transaktion vor dem Commit.
Ein konkretes Szenario
Stellen Sie sich ein orders-Thema und ein inventory-events-Ausgabethema vor. Ihr Dienst liest eine Bestellung, überprüft die SKU und schreibt ein Bestandsabzugsereignis.
Ohne Transaktionen kann ein Absturz nach dem Schreiben der Ausgabe, aber vor dem Commit des Eingabe-Offsets nach einem Neustart einen doppelten Abzug erzeugen. Mit Transaktionen werden das Ausgabeereignis und der Eingabe-Offset-Commit gemeinsam erfolgreich oder schlagen gemeinsam fehl. Ein Neustart kann die Bestellung erneut lesen, aber nur ein committetes Bestandsereignis wird für nachgelagerte read_committed-Verbraucher sichtbar.
Grenzen, die Sie beachten sollten
Kafkas Exactly-Once-Semantik deckt keine Nebenwirkungen außerhalb von Kafka ab, es sei denn, Sie entwerfen dafür. Wenn derselbe Dienst auch in PostgreSQL schreibt oder eine Abrechnungs-API aufruft, benötigt diese externe Nebenwirkung einen eigenen Idempotenzschlüssel, eine eindeutige Einschränkung, eine Transaktionsstrategie oder ein Outbox-Muster.
Transaktionen fügen auch Koordinationsaufwand hinzu. Für die einfache Protokollerfassung, bei der Duplikate akzeptabel sind, können idempotente Produzenten plus At-Least-Once-Verbraucher ausreichen.
Praktische Checkliste
Verwenden Sie eine stabile transactional.id pro Anwendungsinstanz oder Aufgabe. Lassen Sie nicht zu, dass zwei Live-Produzenten gleichzeitig dieselbe Transaktions-ID verwenden.
Stellen Sie Verbraucher von transaktionalen Ausgaben auf read_committed ein. Deaktivieren Sie automatische Offset-Commits in transaktionalen Verarbeitungsschleifen.
Halten Sie Transaktionen kurz. Große Transaktionen können die Latenz erhöhen und die Wiederherstellung verlangsamen.
Behandeln Sie externe Systeme separat. Kafka kann den Kafka-Zustand schützen, aber Ihre Datenbankschreibvorgänge benötigen dennoch ein idempotentes Design.
Die nützliche Erkenntnis: Exactly-Once-Semantik ist kein magischer Schalter. Es handelt sich um eine Reihe von Produzenten-, Verbraucher- und Transaktionsentscheidungen, die am besten für Kafka-zu-Kafka-Stream-Verarbeitung funktionieren.