Apache Kafka, Avro und die Schema Registry


Simon StreubelApache Kafka erfreut sich zunehmend großer Beliebtheit, um Informationsflüsse innerhalb unterschiedlichster IT-Architekturen abzubilden. Sei es im Bereich Big Data, um Echtzeit-Analysen auf Basis von Streaming-Daten durchzuführen, oder im Bereich der Service-to-Service-Kommunikation bei Microservices.

In diesem Blogartikel soll erläutert werden, welche Probleme im Umfeld von Apache Kafka der Einsatz einer Kafka Schema Registry lösen kann und welche Vorteile das Avro Datenformat gegenüber "klassischen" Datenformaten wie JSON oder anderen Binärformaten bietet. Es wird ein Grundwissen über Apache Kafka vorausgesetzt.

Confluent Kafka Schema Registry

Grundsätzlich versteht sich Apache Kafka als eine verteilte Streaming-Plattform, welche Ströme von Nachrichten ähnlich zu Message Queue Systemen oder Enterprise Messaging Systemen in einer fehlertoleranten Art und Weise bereit stellt. Als "Nachrichten" werden von Apache Kafka jegliche Abfolgen von Bytes akzeptiert - seien es (semi-)strukturierte Daten in Form von JSON-Dokumenten, unstrukturierter Text oder ganze Bilder. Bei dem Versenden einer Nachricht findet keine Verifikation der "Korrektheit" der Nachricht statt. Es spielt für Apache Kafka selbst keine Rolle, ob z.B. das Datenschema eines JSON-Dokuments den Erwartungen der Konsumenten einer Nachricht entspricht oder nicht.

Schulung?

Du hast das Gefühl, Du müsstest die Grundlagen von Kafka besser verstehen, um effizienter damit arbeiten zu können? Besuche unsere zweitägige Kafka-Einführungsveranstaltung!


Problemfälle und Best Practices im Betrieb von Apache Kafka

Typische Problemfälle in einem längerfristigen Betrieb einer Apache Kafka Plattform ergeben sich genau aus diesem Umstand. Dinge ändern sich, werden weiterentwickelt und ergänzt oder einzelne Datenfelder innerhalb von JSON-Dokumenten werden umbenannt oder fallen gar ganz weg. Durch die lose Kopplung von Producern und Consumern innerhalb eines Kafka-Ökosystems werden oftmals Konsumenten nicht über anstehende Änderungen notifiziert. Mögliche Lösungen bieten Try-Catch-Konstrukte für sämtliche Parsing-Schritte einer Nachricht, die oft zulasten der Verständlichkeit des Programmcodes gehen. Auch Unternehmensparadigmen wie "Miteinander reden" halten bisweilen nur solange, bis sich Zuständigkeiten und/oder Personen und deren Rollen ändern.

Funktionsweise der Schema Registry als Contractor zwischen Producern und Consumern

Die Confluent Schema Registry löst nun genau dieses Problem. Sie lässt sich als zusätzlicher Baustein in ein bestehendes Kafka-Ökosystem integrieren. Sie erzwingt, dass von den Producern vor dem Versenden einer Nachricht ein Abgleich zwischen dem der Nachricht innewohnenden Datenschema und der Schema Registry erfolgt. (vgl. Abbildung 1). Sollte das Nachrichtenschema mit dem erwarteten Schema überein stimmen, darf der Producer eine entsprechende Nachricht über Kafka versenden. Anderfalls wird das Senden der Nachricht nicht gestattet. Auf diese Weise können Konsumenten einer Nachricht davon ausgehen, dass die von ihnen gelesenen Nachrichten auch wirklich dem Inhalt entsprechen, den sie erwarten.

Schema registry

Abb. 1

Die Schema Registry erfüllt nun also folgende Funktionen:

  • Speicherung eines Schemas,
  • Abruf eines gespeicherten Schemas,
  • Abgleich und Validierung eines Schemas mit einem bereits gespeicherten,
  • Weiterentwicklung und (erneute) Validierung eines Schemas.

Der letztgenannte Punkt ist notwendig, um weiterhin eine gewisse Flexibilität zu gewährleisten. Jedes bereits veröffentlichte Schema kann unter gewissen Kompatibilätsgraden weiter entwickelt werden. So können beispielsweise neue Felder mit sinnvollen Default-Werten hinzugefügt werden, um eine Abwärtskompatibilität zu gewährleisten. Eine Liste der unterstützten Kompatibilitätsgrade findet sich hier.

Libraries zur Ansteuerung der Schema Registry gibt es in nahezu allen gängigen Programmiersprachen (Java, Scala, NodeJS, Go, etc.)

Apache Avro - Deep Dive

Bei Apache Avro handelt sich sich um ein kompaktes, schnell zu konvertierendes, binäres Datenformat, welches im Kontext von Apache Hadoop erstmalig veröffentlicht wurde. Ziel war es, ein Container-Format zu entwickeln, welches erlaubt, Daten persistent und typsicher zu speichern. Dies steht im direkten Kontrast zu den heutzutage allgegenwärtigen JSON-Dokumenten, die weder Typsicherheit und noch feste Strukturen bieten. Daher muss zwangsweise mit jeder Nachricht das eigentlich zugrundlegende Datenschema erneut mitgesendet werden. Darüber hinaus handelt es sich bei JSON-Dokumenten um kein Binärformat, dementsprechend sind JSON-Dokumente im Allgemeinen deutlich speicherintensiver als andere Formate.

Avro Schemas selbst werden in Form von JSON-Dokumenten beschrieben. Hierin können sowohl Feldnamen, Datentypen als auch Dokumentation von einzelnen Datenfeldern festgehalten werden:

JavaScript

{
"namespace": "com.rheindata.example.avro",
"type": "record",
"name": "Employee",
"fields": [
{"name": "name", "type": "string", "doc": "The employees name"},
{"name": "projects", "type": ["int", "null"], "doc": "The employees currect projects"},
{"name": "favorite_color", "type": ["string", "null"], default: "green", "doc": "The employees favorite color"},
{"name":"fields_of_knowledgee",
"type":{"type":"enum",
"name":"fields_of_knowledge_types",
"symbols":["bi","big data","kafka","golang"]},
"doc":"The employees fields of expertise"}
]
}

Mithilfe des oben dargestellten Beispiels lassen sich die eigentlichen Daten nicht nur beschreiben und (de-)serialisieren, sondern auch für die unterschiedlichsten Programmiersprachen entsprechende Klassen/Objekte generieren. Aus Erfahrung lässt sich sagen, dass dies die Entwicklungzeit von neuen Consumern drastisch reduziert, da aufgrund der bestehenden Datenschemata keine entsprechenden JSON-Parser/Objekte mehr von Hand erzeugt werden müssen.

Durch den Einsatz der Schema Registry werden Schemata zentral verwaltet und vorgehalten, sodass diese auch für alle Entwickler eines Unternehmens an zentraler Stelle zugänglich gemacht werden können. Darüber hinaus bietet die Schema Registry noch einen weiteren, unschätzbaren Vorteil: Statt jedes mal das Schema in der eigentlichen Nachricht zu inkludieren, ist es möglich, nur noch eine Schema-ID mit der eigentlichen Nachricht zu versenden. Ein Consumer kann nun vor Verarbeitung der eigentlichen Nachricht über das Senden der Schema-ID an die Schema Registry das entsprechende Schema von dort beziehen und die jeweilige Nachricht dadurch typsicher verarbeiten. Dies verringert die Größe der Nachrichten nochmals, welches wiederum nicht nur den benötigten Speicherplatz, sondern auch die Lese- und Verarbeitungszeit einer Nachricht reduziert.

Avro vs. Thrift vs. Protocol Buffers (ProtoBuf)

Warum sollte nun genau Avro statt anderen binären Datenformaten wie Apache Thrift oder ProtoBuf verwendet werden? Im Gegensatz zu Thrift oder ProtoBuf setzt Avro keine feste Reihenfolge der Datenfelder innerhalb einer Nachricht voraus, da diese nicht über eine Reihenfolge, sondern über Feldnamen referenziert werden. Dies ist in etwa vergleichbar mit dem Zugriff über Namen auf einzelne Datenspalten innerhalb von relationalen Tabellen bzw. Datenbanken, anstelle eines Zugriffs auf einzelne Spalten auf Basis einer festen Reihenfolge wie es z.B. in Excel der Fall ist. Dies ermöglicht gleichzeitig eine gewisse Dynamik in der Weiterentwicklung der Datenstruktur und erleichtert eine Speicherung von Daten in Datenvorhaltesytemen wie Datenbanken, Hadoop oder Druid: bei einer Referenzierung über Reihenfolgen müssten ansonsten alle Daten bei jeder Änderung des Datenmodells erneut gelesen und geschrieben werden, um die Reihenfolge der Datenfelder über Alt- und Neubestände hinweg sicher zu stellen.

Neuer Job?

Kafka, Hadoop und Data Engineering sind Deine Themen, aber bei Deinem aktuellen Arbeitgeber geht's in der Hinsicht nicht recht voran damit? Bewirb Dich als Data Engineer bei der rheindata!


Im Gegensatz zu Thrift oder ProtoBuf trennt Avro auch den Speicherbereich, in dem das Schema gespeichert ist, von dem eigentlichen Inhalt der Datei. Bei Thrift und ProtoBuf wechseln sich immer Feldnummer und Inhalt des Feldes ab, das Schema ist somit stark mit dem Inhalt "verwoben". Durch die Trennung wird hingegen die oben beschriebene Ersetzung des Schemas durch eine Schema-ID ermöglicht.

Der Aufbau einer Nachricht im Avro-Format bei Verwendung der Schema Registry ist dementsprechend wie folgt:

  • Byte 0 - "Magic Byte": Confluent serialization format Versions Nummer (aktuell immer "0")
  • Byte 1-4 - "Schema ID": 4 byte lange Schema-ID, wie von der Schema Registry übermittelt
  • Byte 5... - "Daten": Die eigentlichen serialisierte Daten im Avro binär format

In den meisten Fällen wird das Konstruieren einer derart formatierten Nachricht durch den verwendeten Kafka-Client übernommen. Bei einigen Programmiersprachen muss dieser Teil jedoch durch den jeweiligen Programmcode vom Entwickler implementiert werden. Hier eine Implementierung in Go als Beispiel:

golang

var buf bytes.Buffer

// write MAGIC_BYTE to buffer
buf.WriteByte(byte(0))

// write schema-id to buffer
schemaBinaryId := make ([]byte, 4)
binary.BigEndian.PutUint32(schemaBinaryId, uint32(schemaId))
buf.Write(schemaBinaryId)
// write serialized data to buffer
myAvroObject.Serialize(&buf)

// send buffer content to kafka producer
kafka.produce(buf.Bytes())

Fazit

Durch den Einsatz der Confluent Kafka Schema Registry und Apache Avro ist es möglich, eine gleichbleibende Datenqualität unternehmensweit zu garantieren, die Zusammenarbeit zwischen Teams zu vereinfachen, die Entwicklungszeit zu verringern und Apache Kafka performant und ohne viel Aufwand an Datensenken wie Hadoop, Hive, Presto oder Druid anzubinden.

Sollten Sie Fragen zur Verwendung von Apache Kafka, einer Schema Registry, Avro oder zu sonstigen Themen rund um Hadoop haben, sprechen Sie uns gerne an. Darüber hinaus unterstützen wir Sie auch in allen anderen Themengebieten zu Business Intelligence, Data Warehouse und Data Analytics.