Einführung einer ereignisgesteuerten Architektur für die Bestellverarbeitung
Kontext
Unsere synchrone Bestellverarbeitungs-Pipeline wurde zunehmend zum Engpass. Lange laufende Vorgänge blockierten den Checkout-Prozess, und Ausfälle in nachgelagerten Services verursachten Kaskadeneffekte.
Entscheidung
Migration der Bestellverarbeitung auf eine ereignisgesteuerte Architektur mit Apache Kafka
Betrachtete Alternativen
Bestehenden synchronen Ablauf optimieren
- Keine Architekturänderungen erforderlich
- Team bereits mit der Codebasis vertraut
- Geringeres Risiko
- Löst das grundlegende Kopplungsproblem nicht
- Weiterhin anfällig für nachgelagerte Ausfälle
- Begrenzte Skalierbarkeitsverbesserungen
Einfache Message Queue verwenden (RabbitMQ/SQS)
- Einfacher als Kafka
- Leichter zu betreiben
- Ausreichend für grundlegende asynchrone Verarbeitung
- Keine Möglichkeit zur Ereigniswiederholung
- Begrenzte Aufbewahrung
- Neue Consumer später schwerer hinzuzufügen
Kafka für Event-Streaming verwenden
- Ereigniswiederholung für Debugging und Wiederherstellung
- Einfaches Hinzufügen neuer Consumer
- Hoher Durchsatz und Beständigkeit
- Natürliches Audit-Log
- Betriebliche Komplexität
- Lernkurve für das Team
- Herausforderungen durch eventuelle Konsistenz
Begründung
Das Event-Log-Modell von Kafka bietet Fähigkeiten, die wir mit zunehmendem Wachstum benötigen werden: Wiederholung für Debugging, einfaches Hinzufügen neuer Consumer und ein natürlicher Audit-Trail. Die betriebliche Komplexität ist mit modernem Tooling beherrschbar, und das Team ist bereit, seine Kenntnisse in verteilten Systemen zu erweitern.
Der Wendepunkt
Unser Checkout-Ablauf erledigte zu viel synchron:
- Bestand validieren
- Zahlung verarbeiten
- Bestand aktualisieren
- Bestätigungs-E-Mail senden
- Lager benachrichtigen
- Analytics aktualisieren
Schlug ein Schritt fehl oder war langsam, scheiterte der gesamte Checkout. Am Black Friday führte die Latenz des Zahlungsanbieters zu einer Checkout-Fehlerquote von 30 %.
Ereignisgesteuertes Design
Wir haben das System um Ereignisse herum neu gestaltet:
Bestellung aufgegeben → [Kafka] → Mehrere Consumer
├── Inventory Service
├── Payment Service
├── Notification Service
├── Warehouse Service
└── Analytics Service
Jeder Consumer verarbeitet unabhängig. Ausfälle sind isoliert und werden wiederholt, ohne die Nutzer:innen zu beeinträchtigen.
Herausforderungen bei der Umsetzung
Eventuelle Konsistenz: Nutzer:innen könnten „Bestellung aufgegeben” sehen, bevor der Bestand aktualisiert ist. Wir haben optimistische UI-Updates und klare Statusanzeigen hinzugefügt.
Idempotenz: Consumer müssen doppelte Ereignisse verarbeiten können. Wir haben Idempotenz-Schlüssel für alle Vorgänge implementiert.
Monitoring: Verteiltes Tracing wurde unerlässlich. Wir haben stark in Observability investiert.
Ergebnisse
- Checkout-Erfolgsquote: 99,7 % (zuvor 94 %)
- Durchschnittliche Checkout-Zeit: 800 ms (zuvor 3,2 s)
- Der Black Friday wurde mit dem 3-fachen des bisherigen Spitzenwerts ohne Probleme bewältigt
- Neue Funktionen (Betrugserkennung, Treuepunkte) konnten hinzugefügt werden, ohne den Checkout-Code anzufassen
Die Migration dauerte 4 Monate, verbesserte aber die Resilienz unseres Systems grundlegend.