© MSSA/Shutterstock.com
Einführung in Apache Pulsar – Teil 3

Pulsar Schema Registry


Wann immer zwei Systeme über ein Netzwerk Daten miteinander austauschen, ist die elementare Frage, in welchem Format dies geschehen soll – unabhängig davon, ob dieser Austausch synchron oder asynchron stattfindet. Bei Message-basierten Systemen hat diese Frage einen besonderen Stellenwert, da die teilnehmenden Systeme voneinander so wenig Kenntnis wie möglich haben sollten und die Datenmodellierung somit eine besonders hohe Priorität besitzt. Im Rahmen dieses Artikels möchten wir diskutieren, welche Möglichkeiten Apache Pulsar von Haus aus bietet, um mit Hilfe der enthaltenen Schema Registry einen performanten, zukunftssicheren und einfachen Austausch von Nachrichten zu ermöglichen.

Daten richtig zu modellieren und gegebenenfalls zu versionieren, ist absolut keine neue Problemstellung. Selbst wenn wir den Kontext eines verteilten Systems oder sogar einer Netzwerkverbindung wegnehmen, steht eine Software, die im Laufe ihres Lebenszyklus verschiedene Funktionalitäten ergänzt oder ändert, vor dieser Fragestellung; nämlich mit Hilfe welcher Strategie bereits vorhandene Daten zwischen verschiedenen Softwareversionen kompatibel gehalten werden können. Während in diesem Beispiel die Änderung zentral in nur einem System gelöst werden muss, erhöhen sich der Aufwand und die Problematik um ein Vielfaches, sobald wir eine große Anzahl von Systemen mit unterschiedlichen Technologien und Lebenszyklen haben. Die Serialisierungsform spielt hierbei selbstverständlich ebenfalls eine große Rolle.

Die Frage lautet also: Welche grundlegenden Strategien stehen uns zur Verfügung, welche Kompromisse muss man hierfür eingehen und für welchen Anwendungsfall ist welche Strategie besonders geeignet? Hierzu möchten wir zunächst einen kurzen Überblick über die gängigsten Möglichkeiten zur Daten- und Schemaversionierung geben und andiskutieren, welche Probleme hierdurch entstehen oder gelöst werden, ohne uns hierbei auf die Apache-Pulsar-spezifische Architektur, Eigenschaften oder das Verhalten zu beziehen.

Rein typbasierte Datenmodellierung

Die einfachste aller Möglichkeiten ist eine rein typbasierte Datenmodellierung, bei der alle beteiligten Systeme eine gewisse Datenstruktur für eine Nachricht annehmen. Der grundlegende Typ der Nachricht könnte anhand einer eigenen Property, die zusätzlich zu der Nachricht mit Apache Pulsar ausgeliefert wird, entschieden werden. Dieser Ansatz ist entsprechend simpel in der Implementierung und aus der Sicht der Performanz ebenfalls unkritisch, sofern man die passenden Datenstrukturen verwendet, die eine Serialisierung vorsehen. In einem hypothetischen Beispiel, bei dem alle Systeme auf Java basieren, wäre es eine denkbare Strategie, die Java-eigene Objektserialisierung zu nutzen, um Objektgraphen in einen Bytestream zu transformieren und diesen dann als Nachricht zu publizieren.

Doch selbst wenn wir mit dem Ausschluss aller nicht Java-basierten Systeme leben können, ergeben sich schnell andere Problemstellungen, die diesen Ansatz in den allermeisten Fällen unpraktikabel machen. Wie eingangs erwähnt, liegt die Schwierigkeit der Datenmodellierung in der meist gegebenen Anforderung, eine möglichst hohe Zukunftssicherheit und Kompatibilität sicherzustellen. Eine typbasierte Datenmodellierung (Abb. 1) stößt hierbei sehr schnell an ihre Grenzen. Während das Hinzufügen neuer Felder oder Klassen noch problemlos möglich sein sollte, ist bereits die Änderung eines primitiven Datentyps eine Änderung, die nicht mehr ohne weiteres abbildbar ist. Ebenso wenig möglich wäre es unter anderem, den static Modifier hinzuzufügen oder wegzunehmen, und selbst das Löschen von Feldern kann problematisch sein – von Änderungen an der Klassenhierarchie oder Struktur ganz zu schweigen.

fresow_guenther_pulsar_3_1.tif_fmt1.jpgAbb. 1: Schematische Darstellung typbasierter Datenmodellierung

Es gibt durchaus Lösungsstrategien, um diese Änderungen trotzdem zu ermöglichen. Zum Beispiel über das Muster eines Upcasters, der das Wissen zentral besitzt, wie Typen ineinander zu überführen sind und diese entsprechend aufbereitet, oder aber über das Anlegen eines neuen Typs, der die gewünschten Eigenschaften besitzt. Ab diesem Punkt ist es jedoch bereits fragwürdig, ob man hier nicht am eigentlichen Problem vorbei arbeitet, besonders wenn wir noch weitere Faktoren wie die Zero-Downtime-Fähigkeit von Systemen mit in die Betrachtung einbeziehen. Zusammengefasst bleibt zu sagen, dass eine typbasierte Datenmodellierung für non-triviale Nutzungsfälle ungeeignet ist, da eine Erweiterung oder Änderung des Datenmodells große Probleme und Aufwände verursachen wird.

Strukturierte Datenformate

In Anbetracht der erwähnten Probleme typbasierter Datenmodellierung erscheint es als sinnvoll, sich zunächst über strukturierte Formate Gedanken zu machen, da diese die Übertragung von der systeminternen Repräsentation entkoppeln und weitere Möglichkeiten zur Validierung und Definition über die reinen Typen hinaus bieten. Als Platzhirsche müssen hier selbstverständlich XML und JSON (Abb. 2) erwähnt werden, wobei JSON sich in den letzten Jahren als Quasistandard für den Datenaustausch zwischen Systemen an vielen Stellen etabliert hat. Auf den ersten Blick erscheint es so, als wäre hiermit die Lösung aller unserer Probleme gefunden. Zum einen bieten diese Formate eine breite Unterstützung zwischen verschiedensten Programmiersprachen, zum anderen besteht die Möglichkeit, hier über weitere Metainformationen Hinweise auf die Art und Weise zu geben, wie Daten serialisiert oder deserialisiert werden sollen.

Ein zusätzlicher großer Faktor ist die Möglichkeit, Schemadefinitionen zu erstellen, anhand derer eine Nachricht auf Konformität überprüft werden kann. Hierbei ist nicht mehr allein der reine Datentyp ausschlaggebend, sodass wir grundsätzlich theoretisch eine größere Flexibilität bei gleichzeitiger erhöhter Datensicherheit erhalten. Allerdings bestehen andere Probleme weiterhin. So bieten weder JSON noch XML für sich genommen eine standardisierte Möglichkeit zur Schemaevolution bezüglich Änderungen und Kompatibilitäten am Modell, noch sind beide Formate für ihre besondere Effizienz bei der Speicherung und Übertragung vielfacher strukturell identischer Nachrichten bekannt.

fresow_guenther_pulsar_3_2.tif_fmt1.jpgAbb. 2: Schematische Darstellung strukturierter Datenmodellierung mit JSON

Für die erste Problemstellung ergeben sich – je nach verwendeten Bibliotheken – begrenzt Möglichkeiten zur flexiblen Annotation der Datenklassen. So bietet beispielsweise Jackson die Möglichkeit, über Metainformationen verschiedene Untertypen kenntlich zu machen, aber es existiert keine Möglichkeit, diese zentral zu verwalten oder über alle Systeme auszurollen. Zumindest der Punkt der mangelnden Effizienz kann durch die Verwendung von EXI für XML bzw. BSON für JSON teilweise mitigiert werden, da diese eine effizientere, binäre Repräsentation der Daten ermöglichen. Allerdings muss man sich spätestens hier die Frage stellen, ob das eigentliche Problem adressiert wird, da durch die binäre Repräsentation natürlich auch ein großes Argument für beide Datenformate verloren geht: die einfache Lesbarkeit.

Insgesamt bleiben also immer noch einige Wünsche offen: nämlich die zentrale Verwaltung und Übertragung der passenden Schemainformationen sowie ein klar definierter, eindeutiger Weg, Kompatibilitäten und Migrationswege zwischen unterschiedlichen Datenversionen aufzuzeigen.

Zentral verwaltetes Schema

Mit den Erfahrungen und Einschränkungen aus den vorgenannten Ansätzen ergeben sich zusätzliche Anforderungen an unsere Modellierung, die wir berücksichtigen müssen. Zum einen wollen wir in der Lage sein, unsere Schemainformationen zentral zu verwalten und auszuliefern, zum anderen brauchen wir ein effizientes Datenformat mit einem starken Schema, das ebenfalls eine Beschreibung enthält, wie mit Versionssprüngen aus Sicht eines angebundenen Systems umzugehen ist. Ebenfalls wäre es für viele Anwendungsfälle wünschenswert, wenn wir die Einhaltung des Schemas bereits in der Messaging Middleware bei der Entgegennahme und Bereitstellung der Nachrichten überprüfen könnten.

fresow_guenther_pulsar_3_3.tif_fmt1.jpgAbb. 3: Schematische Darstellung der Datenmodellierung mit Hilfe einer Schema Registry

Um dies zu ermöglichen, benötigen wir drei Komponenten: eine zentrale Schema Registry, ein Serialisierungsformat mit den benötigten Kapazitäten sowie eine Integration beider Dinge in unseren Broker. All das ist im Kontext von Apache Pulsar glücklicherweise gegeben. Zur Serialisierung existieren mannigfaltige Lösungen, die es ermöglichen, ein Datenformat in einer entsprechenden Art und Weise zu beschreiben, sodass nicht nur ein starkes Schema entsteht, sondern auch Migrationspfade zwischen verschiedenen Versionen gegeben sind. Besonders erwähnt seien an dieser Stelle Apache Avro, Apache Thrift sowie Google Protocol Buffers – direkt unterstützt von Apache Pulsar werden Avro und Protobuf. Im weiteren Verlauf werden wir uns auf Avro konzentrieren, eine kurze Einführung in die Eigenschaften und Fähigkeiten von Avro erfolgt ebenfalls später. Zu beachten ist noch, dass Abbildung 3 – wie eingangs erwähnt – sich nicht auf die Architektur von Pulsar bezieht, sondern lediglich eine allgemeine Darstellung beinhaltet, wie sich bei einem schemabasierten Ansatz mit Re...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang