22. Juni 2017 von Tomasz Bieruta
Microservices und Event Sourcing
Komponenten Architektur
Die Zerlegung eines Problems in kleinere Teile, ist die Domäne der Software Architekten. Mir persönlich gefällt die kurze Definition zum Thema der Software Architektur von Helmut Balzert:
„Eine strukturierte oder hierarchische Anordnung der Systemkomponenten sowie Beschreibung ihrer Beziehungen.
Es gibt viele Beschreibungen, was eine Systemkomponente ist. Andreas Andersen bietet euch in seinem Buch zum Thema „Komponentenbasierte Softwareentwicklung“ eine gute Zusammenfassung. Komponenten der Enterrprise Architektur verbergen meist größere Applikationen und können − ähnlich wie die russische Matrjoschka − in weitere Sub-Komponenten aufgeteilt werden.
Applikationen – in diesem Sinne Monolithen – haben durchaus gewisse Vorteile: die Applikationen können schneller entwickelt werden und auch der Betrieb ist einfacher. Ändert sich das Business, dann ändern sich auch die Anforderungen, die entsprechend umgesetzt werden müssen. Mit den Änderungen an den Applikationen werden allerdings oft mehr und mehr Abhängigkeiten eingebaut. Die anfangs gut durchdachte, modulare Architektur beginnt auseinanderzudriften: Entwickler wechseln in andere Unternehmen, dem Nachwuchs fehlt es an notwendigen Kenntnissen, die Wartungskosten steigen und die langen Release-Zyklen − und damit langsame Reaktion auf Marktänderungen − führen zu Spannungen zwischen Fach- und IT-Abteilungen.
Aber wer will heute noch Monolithen bauen? In der letzten Zeit wurden Microservices und Self-contained Systems populär. Eberhard Wolff hat dazu eine gute Zusammenfassung gegeben, die ihr hier nachlesen könnt. Damit habt ihr die Enterprise-Architektur eine Ebene tiefer angewendet, nämlich auf der Applikationsebene. Während bei der Enterprise-Architektur jede Komponente - oder besser gesagt jede Applikation - isoliert und autonom mit eigenen Daten und meist einer Datenbank entwickelt wurde, wird nun die Applikation selbst in mehrere autonome Microservices mit eigenen Daten und Datenbanken zerlegt. Aus einem Monolith wird ein verteiltes System gebaut. Hier tauchen ähnliche Probleme auf, mit denen sich Enterprise Architekten beschäftigen. Dazu zählen unter anderem Security, Kommunikation (also Schnittstellen) und Abhängigkeiten zwischen den Komponenten, Datenkonsistenz oder Datenintegration.
Ich habe meine Erfahrungen mit der Datenintegration gemacht und mich immer wieder folgendes gefragt: Wie werden die Daten aus den einzelnen Microservices in ein Datawarehouse oder andere Reporting-Datenbanken überführt? Ihr wollt ja schließlich auch Analysen und Auswertungen machen oder Daten an eure Kunden liefern.
Die Daten werden aus verschiedenen „Sources“ ausgelesen und in die Zielsysteme repliziert. Die Integration kann mit ETL-Tools im Batch-Modus oder mit Change Data Capture in Echtzeit erfolgen. Diese Art der Datenintegration könnt ihr in vielen Unternehmen finden. Bei der Microservice-Architektur scheint mir die Integration umfangreicher zu sein, da nun die Daten zwischen mehreren Systemen ausgetauscht werden müssen. Wegen der starken Kopplung zu den Datenbanken und deren Schemen sind die Microservices nicht mehr so autonom und isoliert. Bei einer Schemaänderung müssen auch andere Systemkomponenten entsprechend angepasst werden.
Die Services leben nicht isoliert und müssen auch auf Änderungen reagieren können. Wie die Microservices-Kommunikation abläuft, könnt ihr hier nachlesen. Um die Kopplung klein zu halten, setzt man häufig Messaging ein, zum Beispiel JMS. Werden die Daten in der Datenbank gespeichert, könnt ihr Nachrichten an Message Broker senden. Natürlich muss garantiert werden, dass bei Fehlern die Daten konsistent bleiben. Typischerweise werden dafür verteilte Transaktionen, sogenannte 2-Phase-Commit, genutzt. Doch 2-Phase-Commit hat auch Nachteile: ein Ausfall des Transaktionsmanagers kann zu Dead-Locks führen und die Verfügbarkeit des ganzen Systems gefährden.
Event Sourcing
Softwareapplikationen werden gebaut, um Business-Prozesse zu optimieren. In einer Domäne, wie zum Beispiel dem Verkauf, kann der Umsatz dank einer Online Application gesteigert werden. Während des Prozesses werden Daten gesammelt, etwa zum Bestellungseingang oder zur Lieferung, um auf eventuelle Kundenreklamationen reagieren zu können. Mit diesen Reports können die Prozesse dann analysiert und weiter optimiert werden.
Jedes Unternehmen will in der Lage sein, Kundenwünsche zu erahnen. Hast du dich schon mal gefragt, woher Amazon genau weiß, welche Themen oder Bücher dich interessieren? Längst tragen statistische Verfahren und Machine-Learning-Algorithmen zum besseren Verständnis der Kundenbedürfnisse bei.
Neben den typischen Domänen wie dem Verkauf, gibt es Unternehmen, die Rohdaten von Kunden sammeln, sie anreichern, auswerten und die gewonnene Information weiterverkaufen.
Es ist schon erstaunlich, dass viele Unternehmen immer noch Datenverluste akzeptieren. Das möchte ich euch am Beispiel eines Bestellvorganges veranschaulichen: Der Kunde gibt in einem Onlineshop eine Bestellung für ein bestimmtes Produkt auf. Die Bestellung hat unter anderem ein Attribut, nämlich den Status der Anfangswerte wie „pending“, „canceled“ und „closed“. Die Applikation wird erfolgreich ausgeliefert. Später kommen neue Prozessschritte hinzu und der Status bekommt den neuen Wert „accepted“. Nehmen wir nun an, der neue Prozessverantwortliche will nach einigen Monaten auf dem Markt wissen, wieviel Zeit die einzelnen Prozessschritte im Durchschnitt in Anspruch genommen haben. Leider hat er dabei kein Glück, denn der Status wurde bei jeder Aktualisierung überschrieben (Tabellen-Update). Es kann jetzt nur noch der aktuelle Status der Bestellung abgefragt werden. In unserem Beispiel wird das Unternehmen die Daten von nun an historisch ablegen, um ein solches Szenario in Zukunft zu verhindern. Der Punkt ist aber folgender: Wieso unterbindet man nicht gleich die Änderungen an den Rohdaten?
Eine interessante Möglichkeit der Datenhaltung bietet Event Sourcing: Was es damit auf sich hat, ist auch schnell erklärt: der Zustand der Applikation wird als Folge von serialisierten Events persistiert. Es wird ein Logbuch über das geführt, was im System passiert ist. Am Beispiel unserer Bestellung würden Statusänderungen als Nachrichten, nämlich als „OrderStatusChanged“ mit Attributen wie „OrderId“, „OrderStatus“, „Timestamp“ sequenziell gespeichert werden. Der Status der Bestellung kann somit zu jedem Zeitpunkt aus den Events hergestellt werden.
Die serialisierten Events sind nicht für komplexe SQL-Abfragen geeignet. Zu diesem Zweck werden die Events ausgelesen und weiterverarbeitet. Aus den Events können je nach Bedarf verschiedene Views generiert werden. In unserem Beispiel könnte ein Microservice die Statistiken für die Events „OrderStatusChanged“ berechnen.
Der Applikationszustand wird nicht verteilt (in Microservices) gehalten und anschließend zentralisiert, sondern in Form von Nachrichten direkt an ein verteiltes Messaging System mit permanenter Persistenz gesendet. Die Datenintegration ist explizit und ein wichtiger Bestandteil des Gesamtsystems. Mir ist es schon mal passiert, dass ich beim Helpdesk eines großen Unternehmens erfahren musste, dass ich dort immer noch unter meiner alten Adresse gemeldet bin. Wenn ihr dann also hört: „Moment, ich schaue noch in unserem anderen System nach.“, dann wisst ihr warum – die Daten werden nämlich an vielen Orten gepflegt.
Event Sourcing hat noch eine wichtige Eigenschaft. Dank der sequentiellen Speicherung der Events sind verteilte Transaktionen nicht mehr notwendig, um die Datenkonsistenz zu gewährleisten. Verschiedene Systeme können unabhängig voneinander die Events lesen, eigene Daten aktualisieren und mit der Zeit einen konsistenten Zustand erreichen.
Auch der IT-Betrieb kann von Event Sourcing profitieren – etwa wenn eine neue Datenbank oder DB-Version ausgerollt werden muss. Ein Consumer kann die Datenbank ohne Unterbrechung und vor allem im laufenden Betrieb aktualisieren. Irgendwann sind die Daten „up to date“ und die Datenbank kann einfach freigeschaltet werden.
Es gibt bereits einige gute Frameworks auf dem Markt, wie zum Beispiel geteventstore, Eventuate oder AxonFramework, die bei der Implementierung der CQRS – einer Variante der Datenbankabfrage − und der Event-Sourcing-Architektur sehr hilfreich sein können.
Zunehmend setzt sich Kafka als Event Store durch. Denjenigen, die mit Kafka spontan nichts anfangen können, empfehle ich die Seite kafka.apache.org. Ursprünglich wurde Kafka als sehr effizientes, verteiltes Messaging System bei LinkedIn entwickelt. Es ist erstaunlich, welche Datenmengen Kafka verarbeiten kann, wie Todd Palino verdeutlicht: „At the busiest times of day, we are receiving over 13 million messages per second, or 2.75 gigabytes of data per second. To handle all these messages, LinkedIn runs over 1100 Kafka brokers organized into more than 60 clusters. “
Kafka speichert die Nachrichten auf der Festplatte für einen konfigurierbaren Zeitraum oder für eine bestimmte Datenmenge. Diese Konfiguration kann auch ausgeschaltet werden, womit eine permanente Speicherung der Daten erreicht werden kann. Consumer lesen die Daten aus und „commiten“ die gelesene Position. Dank der Persistenz der Nachrichten ist die Performance der Event Producers nicht von der Performance der Consumers beeinträchtigt. Die Consumers müssen auch die Nachrichten nicht in Echtzeit verarbeiten. Fällt ein Consumer aus, kann er nach dem Restart seine Arbeit, ausgehend von der letzten Position, fortsetzen. Neben Client API (Producer und Consumer) bietet Kafka Connect API und Kafka Streams API für die Event-Stream-Verarbeitung. Mit Connect API kann Kafka Daten zwischen den Datenbanken replizieren. Kafka Streams eignet sich vor allem für Datenaggregation und -transformation. Dabei werden die Daten aus Kafka gelesen und in Kafka geschrieben. In vielen Fällen benötigt die Stream-Verarbeitung einen Zustand, der lokal gespeichert wird – einen sogenannten state store. Mit der Einführung der „Interactive Queries“ ist es nun möglich, den aktuellen Zustand der Streams abzufragen. Das hat einen enormen Vorteil, denn so lässt sich eine Microservice-Architektur deutlich einfacher umsetzen. Ein Microservice kann Nachrichten an die Kafka-Cluster schicken und mit Kafka-Streams kann der aktuelle Applikationszustand lokal reproduziert werden. Für komplexere Datenanalysen in Echtzeit kann Apache Spark mit integrierter Unterstützung für Machine-Learning-Algorithmen verwendet werden.
Fazit
Mit der Kafka-Messaging-Plattform kann nicht nur eine eventbasierte Architektur umgesetzt, sondern vor allem die Daten zentralisiert und als eine globale, zuverlässige Informationsquelle verwaltet werden. Neue Anforderungen können schneller umgesetzt und sogar im laufenden Betrieb freigeschaltet werden.
In Event Sourcing wird der aktuelle Applikationszustand aus allen historischen Events abgeleitet. Sollte eine Entity sehr viele Events haben, kann dies zu langsameren Antwortzeiten führen. Oft bieten verschiedene Frameworks Optimierungen, sogenannte Snapshots, an. Dank der Kafka „Interactive Queries“ ist der aktuelle Zustand in Echtzeit verfügbar.
Mit Hilfe von Microservices, unter Einbezug von Kafka als Event Sourcing, lassen sich flexible und skalierbare Architekturen umsetzen.