News

Trans­ak­tio­na­le Ver­ar­bei­tung mit Apache Camel

Kommunikation mit Zuversicht

Im dynamischen Umfeld des Bankwesens ist der Verlust bedeutender Nachrichten ein ernsthaftes Problem. Besonders in Systemen, die auf einer Event-Driven Architecture basieren, kann dieses Risiko schnell zur Realität werden. Kürzlich stand ich vor der Herausforderung, genau diese Problematik für einen unserer Kunden zu untersuchen. In diesem Artikel möchte ich nicht nur die Lösung meiner Analyse vorstellen, sondern auch praxisnahe Einblicke bieten, wie die Datenzuverlässigkeit effektiv gesteigert werden kann.

Die Herausforderungen der Event-Driven Architecture

Die Event-Driven Architecture bietet zahlreiche Vorteile. Das Entkoppeln von Microservices und die asynchrone Kommunikation durch einfache Nachrichten ermöglichen die effiziente Bewältigung von Stolpersteinen wie Skalierbarkeit, geringer Latenzzeit, Ausfallsicherheit und Wartbarkeit.

Allerdings birgt sie auch Herausforderungen, die sorgfältig berücksichtigt werden müssen. Nachrichten werden in der Regel nach dem «Fire-and-Forget»-Prinzip versendet. Die Gewährleistung, dass diese ihr Ziel erreichen und korrekt verarbeitet werden oder sogar den nächsten Message Broker erreichen, der die Nachrichten persistent speichert, wird nicht automatisch sichergestellt. Auf dem Weg können viele Dinge schiefgehen: Services können ausfallen, das Netzwerk kann versagen usw.

Im besten Fall werden Fehler irgendwo im Log erfasst, was jedoch mit aufwendigem Monitoring verbunden ist. Im schlimmsten Fall verschwinden die Nachrichten einfach spurlos.

Im Bankensektor können solche Fehler schnell schwerwiegende Konsequenzen haben, sei es bei der Abwicklung von Finanzprodukten, beim Ausführen von Zahlungen oder zur Erfüllung regulatorischer Anforderungen.

Die Lösung: Apache Camel

Apache Camel stellt ein leistungsfähiges Integrationsframework zur Verfügung, das viele Funktionen «out of the box» als einzelne Komponenten bereitstellt, die wie Legosteine flexibel kombiniert werden können. Routing, Transformation, Anreicherung von Nachrichten sowie die Verbindung zu einer Vielzahl von Services werden durch diese vorgefertigten Komponenten erleichtert.

Mit einer einfachen Konfiguration in Apache Camel kann man beispielsweise transaktional von Apache Kafka oder anderen Message Queues konsumieren. Hierbei bedeutet transaktional, dass im Falle eines Fehlers der Zustand zurückgesetzt wird und somit keine Nachrichten verloren gehen. Apache Camel schafft dies, indem es:

1. die Nachrichten liest (1) und sie zur REST-Schnittstelle weiterleitet (2), jedoch erst als abgeschlossen markiert, wenn die Bestätigung einer erfolgreichen Verarbeitung (3) vom Endpunkt zurück zu Kafka gelangt (4).

2. fehlerauslösende Nachrichten auffängt und an einem zentralen Ort sammelt. Wie die Grafik unten zeigt, liest Camel eine Nachricht (1) und leitet sie weiter (2). Die REST-Schnittstelle antwortet jedoch mit einem 400-HTTP-Fehlercode (3). Die Nachricht wird somit in ein separates Kafka-Topic zurückgeschickt.

Nachrichten, die aufgrund von Timeouts verschwinden und daher keine Antwort herbeiführen (weder positiv noch negativ), werden nach einem bestimmten Zeitintervall erneut verschickt.

Dieses Transaktionsprinzip ist vergleichbar mit dem aus Datenbanken bekannten Ablauf: START TRANSACTION, um eine Transaktion zu beginnen, gefolgt von COMMIT, um die Daten zu persistieren, oder ROLLBACK, um die Daten zurückzusetzen.

Damit wird sichergestellt, dass jede Nachricht entweder ihr Ziel erreicht oder an einem zentralen Ort gesammelt wird, falls ein Fehler auftritt.

Wo ist der Haken?

Leider bringt diese Lösung auch Nachteile mit sich. Die Einführung von Apache Camel führt zwangsläufig zu einer erhöhten Komplexität, insbesondere, wenn es als neues Framework eingeführt wird. Apache Camel ist wiederum auch in der Lage, viele Integrationsprobleme zu lösen, wodurch die Gesamtkomplexität an anderen Stellen reduziert wird. Bei Oepfelbaum haben wir Apache Camel bereits erfolgreich bei Kunden eingesetzt und konnten damit äusserst effiziente und robuste Integrationen von Anwendungen realisieren.

Ein weiterer eher negativer Aspekt besteht in der verminderten Datenrate. Eine einzige Nachricht kann die Kommunikation blockieren, solange sie in der Pipeline verarbeitet wird. Das heisst: Die nächste Nachricht wird nicht sofort verschickt, nachdem die erste verarbeitet wurde. Stattdessen wird auf das Feedback gewartet. Dennoch besteht mit Kafka die Möglichkeit, durch die Verwendung mehrerer Partitionen eine Parallelisierung zu erreichen, um diese Symptome zu mindern.

Diese Lösung garantiert ausserdem eine «At-least-once»-Lieferung. Das bedeutet, dass jede Nachricht mindestens einmal verarbeitet wird. Es kann jedoch vorkommen, dass eine Nachricht zweimal gesendet wird (zum Beispiel, wenn eine Feedbacknachricht nicht beim Sender ankommt). Um eine doppelte Ausführung zu verhindern, müssen die Nachrichten idempotent verarbeitet werden. Ob die Implementierung dieser Lösung sinnvoll ist, hängt von verschiedenen Faktoren ab.

Die Implementierungskosten können erheblich sein, insbesondere, wenn Apache Camel noch nicht im Einsatz ist. Es muss ausserdem bewertet werden, wie kritisch die erfolgreiche Zustellung der Nachrichten ist. Welche Kosten entstehen, wenn eine Nachricht nicht korrekt verarbeitet wird? Wie viele Nachrichten gehen mit der aktuellen Lösung verloren? Eine gründliche Analyse ist unerlässlich, wenn man eine solche Lösung in Betracht zieht. Oepfelbaum ist Ihr Partner für die Beantwortung dieser Fragen und steht Ihnen bei der intensiven Analyse Ihrer Systeme stets zur Seite.

Alles nur Theorie ... oder?

Die technische Umsetzung mit Apache Camel ist fast schon trivial. Um diese Aussage zu untermauern, habe ich einen Prototyp entwickelt, der Apache Kafka mit IBM Message Queue und einem REST-Server transaktional verbindet. Apache Camel läuft auf einer Spring-Boot-Instanz, während die restlichen Services entweder manuell oder mithilfe von Docker Compose gestartet werden können:

Link zum Github Repository

git clone https://github.com/Oepfelbaum/transactional-camel

cd transactional-camel

docker compose up -d

Hinweis: Das Docker Image für IBM MQ ist nicht kompatibel mit Apple Silicon (ARM).
Alternativen:

  • Selbst eine Developer Image für ARM bauen
  • Eine x86_64 Podman VM aufsetzen
  • Auf eine Maschine mit x86_64 wechseln

Kontakt