Artikelstammdaten mit Marktplatzhändlern synchronisieren

Ein international tätiger Großhandelskonzern stand zu Beginn seines Projektes „B2B Marktplatz“ vor der Herausforderung, die Artikelstammdaten der neu einzuführenden Marktplatz Plattform MIRAKL mit dem bestehenden, unternehmensweiten Webshop zu synchronisieren. Ziel war es, den Kunden des etablierten Online Shops ein breiteres Sortiment anzubieten, d.h. neben dem eigenen Kernsortiment wollte man weitere Artikel von ausgewählten Marktplatzhändlern offerieren. Um dies zu ermöglichen, sollte ein „Stammdaten-Adapter“ die über den Online-Marktplatz von den Verkäufern bereitgestellten Artikelstammdaten und digitalen Assets wie Produktbilder und Videos validieren, deduplizieren und dem Webshop zur Verfügung stellen. D.h. neue Stammdaten oder auch Updates auf bestehenden Artikeldaten der Marktplatzhändler müssen in real-time dem Webshop übergeben werden.  Die Händler können so jederzeit Updates auf mehreren tausend Artikeln bereitstellen und eine kurzfristige Aktualisierung der Informationen im Online Shop erwarten.

Das Projekt-Team der MT AG

Die MT AG unterstützte das Projekt “B2B Marketplace” im Umfeld der Stammdaten mit dem zu realisierenden Produkt „Masterdata Adapter for Marketplace“ von Beginn an maßgeblich. Von der ersten Anforderungsanalyse und der Konzeption des Systems bis zum GoLive stellte die MT AG den Product Owner und das Entwicklungsteam. Das Team der MT AG setzte dabei auf ein agiles Vorgehen und modernste Java-Technologien. In enger Abstimmung mit dem Fachbereich und sonstigen Stakeholdern wurde zunächst der MVP („Minimum Viable Product“) Scope, also das „minimal lebensfähige Produkt“ definiert und in kurzen Sprint-Zyklen konzipiert, implementiert, rundum automatisiert getestet und nach der Produktivsetzung agil weiterentwickelt.

Microservice-Architektur auf Basis von Apache Kafka

Das grundlegende Architekturprinzip des „Stammdaten Adapters“ sind Spring Boot-basierte Microservices, die über Apache Kafka kommunizieren und so event-getrieben die Businesslogik abbilden. Jeder Service übernimmt damit eine spezifische Aufgabe, wie z. B. eine Entscheidungsfindung, eine Datenbereitstellung oder eine Überprüfung/ Validierung, wobei die Kommunikation zwischen den Services stets asynchron über Kafka Topics erfolgt. Die Services lassen sich generell in Verarbeitungs-/Verhaltensmuster ähnlich zu Enterprise-Integration-Patterns (wie z.B. in Apache-Camel) klassifizieren. Unter anderem wurden für die folgenden Messaging Pattern Programmiermuster entwickelt, die den Services trotz unterschiedlicher fachlicher Aufgaben zugrunde liegen. Dieses Konzept erhöht die Wartbarkeit und bietet die Möglichkeit, die Funktionalität schnell und einfach zu erweitern.

Messaging Patterns
Abbildung 1: Messaging Patterns

Reaktive Programmierung

Alle Services sind gemäß dem reaktiven Programmierparadigma implementiert. D.h. die Kafka-Kommunikation, der Zugriff auf die Apache Cassandra Datenbank und die Anbindung von Fremdsystemen über REST-Schnittstellen wurden „non-blocking“ implementiert. Zum Einsatz kommen Spring Boot mit seinen reaktiven Komponenten WebFlux und reaktiven Cassandra-Repositories.  Apache-Kafka wird direkt über die reaktiven Komponenten von Project Reactor angesteuert.  Die Vorteile dieser non-blocking-Programmierung zeigen sich nicht nur bei der Performance der implementierten Services, sondern auch z.B. bei geringem Durchsatz von angebundenen Umsystemen: Über „reactive back-pressure“ wird die Verarbeitungsgeschwindigkeit automatisch gedrosselt, die Systeme bleiben stabil und die Verarbeitung kommt nicht komplett zum Erliegen. Durch die Verwendung des reaktiven Programmiermodells wird der erstellte Code sehr schlank und aussagekräftig und erreicht damit ein sehr hohes Abstraktions-Niveau.

Visualisierung von Events der Verarbeitung von 6000 Updates in Kibana
Abbildung 2: Visualisierung von Events der Verarbeitung von 6000 Updates in Kibana

Fail-Fast und Idempotenz

Idempotenz bildet die Grundlage für die komplette Verarbeitung. Auch die Kommunikation mit den Umsystemen ist idempotent ausgelegt. Damit können im Fehlerfall Daten einfach fallengelassen werden und eine komplette erneute Verarbeitung kann angestoßen werden. Dies vereinfacht die Fehlerbehandlung für jeden einzelnen Microservice und bietet die Möglichkeit, auch manuell auf Ausfallzeiten zu reagieren oder Daten erneut über das System bereitzustellen.

Zustandslose Verarbeitung

Grundsätzlich arbeiten alle Services zustandslos, lediglich eine erneute Verarbeitung derselben Daten kann über eine ‘bereits-erfolgreich-bearbeitet‘-Strategie ausgeschlossen werden. Die zustandslose Verarbeitung erleichtert insbesondere die Umsetzung/Behandlung von Fehlersituationen, da nur der „einfache Gutfall“ berücksichtigt werden muss.

Continuous Integration and Deployment

Die Entwicklung erfolgt lokal auf Entwickler-PCs mit der IDE IntelliJ Idea. Die einzelnen Microservices werden per Jenkins-Pipelines direkt in der Google-Cloud-basierten Kubernetes/ Docker-Infrastruktur bereitgestellt. Jeder git-Commit triggert einen neuen Job auf der jeweiligen Pipeline, so dass nach erfolgreichem Build und automatisierten Tests die direkte Bereitstellung des Service für die verschiedenen Stages wie Development, Pre-Produktion und auch (nach manueller Freigabe) in Produktion erfolgt.

Teststrategien

Damit eine automatisierte und qualitätsgesicherte Auslieferung der Microservices in Produktion über die Build-Pipeline möglich ist, ist eine hohe automatisierte Testabdeckung der einzelnen Services, aber auch des Zusammenspiels der Services von immenser Wichtigkeit. Das entwickelte Testkonzept erstreckt sich über mehrere Level der Test-Pyramide:

  • Unit-Tests
    Mit Hilfe von JUnit 5 und JMockit testen die Unit-Tests auf Klassenebene unabhängige Funktionalitäten wie Mappings oder Strategiepattern.
  • Integrations-Tests:  In-Process
    In-Prozess-Tests auf Serviceebene testen möglichst vollständig die Verarbeitung eines Service ab. Die unterschiedlichen Reaktionen der Umsysteme werden über Mocks innerhalb der Spring-Boot-Test-Umgebung bereitgestellt. Diese Tests stellen den größten Aufwand in der Entwicklung dar, da hier möglichst die Vollständigkeit der Testszenarien angestrebt wird.
  • Integrations-Tests:  Out-Of-Process
    Out-Of-Prozess-Integrationstests testen eine konkrete Instanz des Service in der Development Stage. Test-VM und Service-VM sind hier getrennt, so dass nur die Schnittstellen eines Service getestet werden können (z. B. die Reaktion des Service auf die Bereitstellung eine Kafka-Message in einem speziellen Topic)
  • End-2-End Tests in Pre-Produktion
    End-2-End-Tests testen automatisiert Microservice-übergreifend das Gesamtsystem und benutzen die realen Umsystem-Schnittstellen (in der Pre-Produktion) und verifizieren die Integration der Adapter-Software in die bestehende Systemlandschaft. Hier gibt es eine Abhängigkeit zu den konkreten (Test-)Umsystemen, so müssen unter anderem auch konsistente Test-Daten bereitgestellt werden.
  • Lokale End-2-End Tests
    Die Abhängigkeit zu den Umsystemen entfällt für lokale End-2-End-Tests auf dem Entwickler-Rechner.  Das eingesetzte Citrus-Testframework bietet eine einfache Möglichkeit, für sämtliche Umsystem-Anfragen des lokalen Gesamtsystems Mocks bereitzustellen. Diese Mocks können verhältnismäßig einfach jede denkbare Antwort von Umsystemen simulieren und sind damit unabhängig von echten Umsystemen. Damit lässt sich einfach eine Vollständigkeit in den Testszenarien erreichen. Typischerweise wird pro Use-Case ein ‚Citrus-Szenario‘ bereitgestellt und getestet.

Mit der in den Build-Prozess integrierten Test-Automatisierung über alle Testlevel wird fortlaufend die Qualität gewährleistet und die Funktionsfähigkeit quasi “live”/pro commit sichergestellt.

Monitoring und Self Healing im operativen Betrieb


Die Synchronisierung der Systeme wird über einen Polling-Mechanismus initiiert. Bei diesen zyklischen Abfragen werden auch „Blind-Daten“ so genannte „Monitor-Items“ durch die Microservice-Ketten geschickt, die jedoch keine echte Verarbeitung auslösen, aber Abfragen an die operationalen Umsysteme stellen.  Ein Ausbleiben der periodischen Verarbeitung von normalen bzw. Monitor-Items deutet auf eine Fehlersituation hin; z.B. könnte das „Lauschen“ an einem Kafka-Topic aus irgendeinem Grund gestoppt worden sein. Kann ein Service über einen bestimmten Zeitraum kein Item erfolgreich verarbeiten, so wird dies von der Kubernetes-Health-Erkennung registriert und der Service bzw. der entsprechende Container wird neu gestartet.

Visualisierung der Verarbeitung der Monitor-Items in Kibana
Abbildung 3: Visualisierung der Verarbeitung der Monitor-Items in Kibana

Zusammenfassung / Ausblick

Die Entwicklung von Microservices und die reaktive Kommunikation über Kafka und zu Umsystemen stellt zum heutigen Tag die State-of-The-Art Software-Entwicklung in der Java-Backend-Entwicklung dar. Mit der ELK-Stack-Integration und Kubernetes erreicht man ohne große Aufwände exzellente Monitoring-Möglichkeiten, um Fehlersituationen oder Spitzenlast-Situationen zu analysieren.

Das System ist seit Februar 2019 in Produktion und hält auch unerwarteten Spitzenlasten stand. Die erstellte Integrationslösung erweist sich im Betrieb als sehr stabil, fehlertolerant und einfach erweiterbar. Das System wurde an ein „internes“ Entwicklerteam des Kunden übergeben und wird stetig weiterentwickelt und um neue Anforderungen ergänzt. Die hier beschriebenen Architektur- und Test-Konzepte werden von dem Handelskonzern als Blueprint für neue Microservice Implementierungen verwendet.

Bei Fragen wendet Euch bitte an Bernd Focke, Thomas Greve oder Peter Wedekind.


 

Jetzt teilen auf:

Jetzt kommentieren