© Excellent backgrounds/Shutterstock.com
Strukturierte Daten in Kafka mit Apache Avro und der Schema Registry

Kafka 101


Bei der Definition von Schnittstellen ist die Spezifizierung von Datenstrukturen ein wichtiges Thema. Eine falsche Wahl kann später schnell zu Problemen führen, und obwohl man eine lose Kopplung von Komponenten geplant hat, trotzdem zu starken Abhängigkeiten und somit in einem hohen Koordinationsaufwand resultieren. Das übliche Nutzungsverhalten von Apache Kafka, bei dem Nachrichten einmalig publiziert und anschließend mehrfach konsumiert werden, kann diesen Effekt zusätzlich verstärken und bei Formaten mit suboptimaler Leseperformance den negativen Einfluss auf die Performance multiplizieren.

Kafka stellt mit der Schema Registry ein zentrales REST-API für die Abfrage und Verwaltung aktueller, sowie historischer Definitionen von Datenstrukturen, deren Avro-Schema, zur Verfügung. Nach einer kurzen Einführung in Avro wird gezeigt, wie mithilfe der Schema Registry Avro-Schemas definiert und ausgetauscht werden können. Im Folgenden wird im Rahmen dieses Artikels auf die Vorteile einer zentralen Schemaverwaltung eingegangen und die einseitige Evolution eines Schemas gezeigt, bei der sich der Producer oder Consumer weiterentwickelt und der jeweilige Gegenpart davon nicht negativ beeinflusst wird.

Serialisierung

Zur Übertragung über das Netzwerk oder zur Persistierung von Daten ist es notwendig, diese zu serialisieren, d. h., strukturierte Daten in einen sequenziellen Datenstrom zu überführen. Zum Empfangen bzw. Auslesen des Datenstroms muss dieser anschließend wieder in eine strukturierte Datenform umgewandelt werden. In der Praxis bedeutet dies oft, dass eine Domäne auf der einen Seite mit Metainformationen in Code und Persistenzsystem abgebildet wird, hier also inhärent das Schema präsent ist und auf der anderen Seite während der Datenübertragung schemalose Formate wie JSON zum Einsatz kommen. Dies kann bei verteilten Systemen schnell zu Problemen führen, da sich unter Umständen nur in Teilen des Systems die Datenstruktur ändert (beispielsweise beim testweisen Ausrollen von Upgrades auf einzelnen Knoten). Schemalose Formate bergen die Gefahr, dass durch mangelhafte Dokumentation und Beschreibung der Daten Informationen bei der Übertragung verlorengehen bzw. unbrauchbar werden oder auch Inkompatibilitäten erzeugt werden. Formate mit Schema wie bspw. XML adressieren das erste Problem – die Gefahr der auftretenden Inkompatibilität bei Veränderungen in Teilen des Systems bleibt jedoch bestehen.

Zusammenfassend beschreibt Martin Kleppmann vier „Evolutionsphasen“ bezüglich der Domänenserialisierung [1]:

  • Benutzung des nativen Frameworks der jeweils verwendeten Programmiersprache.

  • Verwendung eines sprachenunabhängigen Formats wie JSON oder XML.

  • Binäres JSON wie BSON [2], MessagePack [3] oder der Binary JSON Serializer aus Voldemort [4].

Trennung von Schema (inkl. Dokumentation) und Speichern der eigentlichen Daten in einem kompakten Binärformat. Gängige Serialisierungsformate und Frameworks der letzten Kategorie sind Protocol Buffers [5], Thrift [6] und das von Kafkas Schema Registry verwendete Avro [7].

Rückblick

Kafka ist ein verteilter, partitionierender und replizierender Service für Datenströme. Es stellt seine Funktionen wie klassische Messaging Broker zur Verfügung, allerdings unter der Verwendung von Konzepten, die sich deutlich unterscheiden. Eine ausführliche Einführung bietet unser Artikel „Massive Datenströme mit Apache Kafka“ aus der Java-Magazin-Ausgabe 8.2015. Dort gehen wir auf die grundlegenden Komponenten und Konzepte ein und zeigen an einem konkreten Anwendungsbeispiel, wie diese zu verwenden sind.

Bei datenintensiven Anwendungen wirkt sich die gewählte Form der Serialisierung auch direkt auf weitere Eigenschaften des Systems aus. Neben offensichtlichen Faktoren wie Größe und Datenmenge spielt zudem die Art des Zugriffs eine Rolle (leseintensiv versus schreibintensiv) und sollte in die Wahl mit einbezogen werden.

Confluent-Plattform

LinkedIns ehemaliges Hauptentwicklerteam von Kafka hat sich mit dem Unternehmen Confluent Inc. erst kürzlich selbstständig gemacht, um kommerzielle Produkte und Dienstleistungen rund um Kafka zu entwickeln und anzubieten. Alle Komponenten sind unter der Apache-Lizenz 2 veröffentlicht. Die aktuellen Hauptbestandteile der so genannten Confluent-Plattform (Abb. 1) sind:

  • Apache Kafka

  • Die Schema Registry, welche zur Verwaltung von Avro-Schemas für Kafka-Topics eingesetzt wird

  • Ein REST Proxy, der es erlaubt, Kafka-Nachrichten via REST zu versenden und zu konsumieren

  • Camus, einem Map/Reduce-Job, um Nachrichten aus Kafka in HDFS abzulegen

pfannenschmidt_schemaregistry_1.tif_fmt1.jpg Abb. 1: Confluent-Plattform nach [8]

Schemaänderungen

Hat man sich für ein Serialisierungsformat mit einem Schema entschieden, muss ein weiterer wichtiger Punkt beachtet werden: Schemaänderungen. Eine gute Einführung in die Problematik gibt Gwen Shapira [9]. Hierbei ist die Frage nicht, ob sich das Schema ändern wird, sondern wann dies der Fall ist, unabhängig, ob durch zusätzliche Anforderungen an die Domäne, Code Refactoring oder neu gewonnene Erkenntnisse im Umgang mit dem entwickelten Produkt hervorgerufen werden.

Anwendungsbeispiel

Zur Veranschaulichung der verschiedenen Konzepte und Funktionen soll im Folgenden das stark vereinfachte Anwendungsbeispiel zum Thema Clickstream dienen [10]. Diesen Datenstrom wollen wir über Kafka mittels strukturierter bzw. schemabehafteter Nachrichten sowohl senden als auch empfangen. Wir beschränken uns in der Domäne auf die selbsterklärende Klasse Click aus Listing 1, die einen Seitenaufruf eines Besuchs auf einer Webseite widerspiegelt. Mit dieser Domäne und der einführenden Betrachtung zu Serialisierungsframeworks soll nun das von Kafkas Schema Registry verwendete Avro genauer beleuchtet werden.

Listing 1

package io.kafka101.clickstream.schema.domain; public final class Click { public final String time, ip, page; public Click() { this.time = this.ip = this.page = null; } public Click(String time, String ip, String page) { this.time = time; this.ip = ip; this.page = page; } }

Schemadefinition in Avro

Ein Schema wird in Avro über eine JSON-Datei mit der Endung avsc deklariert und kann sowohl manuell gepflegt als auch aus den Java-Klassen der Domänenschicht automatisch generiert werden. Dazu sind im Package org.apache.avro.reflect neben Klassen mit reflektiver Funktionalität, welche im Anwendungsbeispiel später im AvroTranslator (Listing 5) zum Einsatz kommen, auch Annotationen, die ähnlich wie Jackson-Annotationen verwendet werden können, zu finden. Umgekehrt lässt sich entweder über das A...

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

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang