Anleitung zu Apache Avro

1. Übersicht

Die Datenserialisierung ist eine Technik zum Konvertieren von Daten in ein Binär- oder Textformat. Zu diesem Zweck stehen mehrere Systeme zur Verfügung. Apache Avro ist eines dieser Datenserialisierungssysteme.

Avro ist eine sprachunabhängige, schemabasierte Datenserialisierungsbibliothek . Es verwendet ein Schema, um Serialisierung und Deserialisierung durchzuführen. Darüber hinaus verwendet Avro ein JSON-Format, um die Datenstruktur anzugeben, die es leistungsfähiger macht.

In diesem Tutorial erfahren Sie mehr über das Avro-Setup, die Java-API zur Durchführung der Serialisierung und einen Vergleich von Avro mit anderen Datenserialisierungssystemen.

Wir konzentrieren uns hauptsächlich auf die Schemaerstellung, die die Basis des gesamten Systems bildet.

2. Apache Avro

Avro ist eine sprachunabhängige Serialisierungsbibliothek. Zu diesem Zweck verwendet Avro ein Schema, das eine der Kernkomponenten darstellt. Es speichert das Schema in einer Datei zur weiteren Datenverarbeitung .

Avro eignet sich am besten für die Big Data-Verarbeitung. Es ist in der Hadoop- und Kafka-Welt wegen seiner schnelleren Verarbeitung sehr beliebt.

Avro erstellt eine Datendatei, in der Daten zusammen mit dem Schema in seinem Metadatenabschnitt gespeichert werden. Vor allem bietet es eine umfangreiche Datenstruktur, die es beliebter macht als andere ähnliche Lösungen.

Um Avro für die Serialisierung zu verwenden, müssen wir die unten genannten Schritte ausführen.

3. Problemstellung

Beginnen wir mit der Definition einer Klasse namens AvroHttRequest , die wir für unsere Beispiele verwenden werden. Die Klasse enthält sowohl primitive als auch komplexe Typattribute:

class AvroHttpRequest { private long requestTime; private ClientIdentifier clientIdentifier; private List employeeNames; private Active active; } 

Hier ist requestTime ein primitiver Wert. ClientIdentifier ist eine weitere Klasse, die einen komplexen Typ darstellt. Wir haben auch employeeName, was wiederum ein komplexer Typ ist. Aktiv ist eine Aufzählung, um zu beschreiben, ob die angegebene Liste der Mitarbeiter aktiv ist oder nicht.

Unser Ziel ist es, die AvroHttRequest- Klasse mit Apache Avro zu serialisieren und zu de-serialisieren .

4. Avro-Datentypen

Bevor wir fortfahren, wollen wir die von Avro unterstützten Datentypen diskutieren.

Avro unterstützt zwei Arten von Daten:

  • Primitiver Typ: Avro unterstützt alle primitiven Typen. Wir verwenden den primitiven Typnamen, um einen Typ eines bestimmten Feldes zu definieren. Zum Beispiel kann ein Wert, der eine hält String in Schema: sollte als { „string“ „type“} deklariert werden
  • Komplexer Typ: Avro unterstützt sechs Arten komplexer Typen: Datensätze, Aufzählungen, Arrays, Karten, Gewerkschaften und feste

In unserer Problemstellung ist ClientIdentifier beispielsweise ein Datensatz.

In diesem Fall sollte das Schema für ClientIdentifier folgendermaßen aussehen:

{ "type":"record", "name":"ClientIdentifier", "namespace":"com.baeldung.avro", "fields":[ { "name":"hostName", "type":"string" }, { "name":"ipAddress", "type":"string" } ] }

5. Verwenden von Avro

Fügen wir zunächst die benötigten Maven-Abhängigkeiten zu unserer Datei pom.xml hinzu .

Wir sollten die folgenden Abhängigkeiten einbeziehen:

  • Apache Avro - Kernkomponenten
  • Compiler - Apache Avro Compiler für Avro IDL und Avro Specific Java APIT
  • Tools - einschließlich Apache Avro-Befehlszeilentools und Dienstprogramme
  • Apache Avro Maven Plugin für Maven Projekte

Wir verwenden Version 1.8.2 für dieses Tutorial.

Es wird jedoch immer empfohlen, die neueste Version auf Maven Central zu finden:

 org.apache.avro avro-compiler 1.8.2   org.apache.avro avro-maven-plugin 1.8.2 

Nach dem Hinzufügen von Maven-Abhängigkeiten sind die nächsten Schritte:

  • Schemaerstellung
  • Lesen Sie das Schema in unserem Programm
  • Serialisierung unserer Daten mit Avro
  • Zum Schluss de-serialisieren Sie die Daten

6. Schemaerstellung

Avro beschreibt sein Schema im JSON-Format. Es gibt hauptsächlich vier Attribute für ein bestimmtes Avro-Schema:

  • Typ - beschreibt den Typ des Schemas , unabhängig davon, ob es sich um einen komplexen Typ oder einen primitiven Wert handelt
  • Namespace - Beschreibt den Namespace, zu dem das angegebene Schema gehört
  • Name - Der Name des Schemas
  • Felder - Hier werden die Felder angegeben, die einem bestimmten Schema zugeordnet sind. Felder können sowohl primitiven als auch komplexen Typs sein .

Eine Möglichkeit zum Erstellen des Schemas besteht darin, die JSON-Darstellung zu schreiben, wie wir in den vorherigen Abschnitten gesehen haben.

Wir können auch ein Schema mit SchemaBuilder erstellen, was zweifellos eine bessere und effizientere Möglichkeit ist, es zu erstellen.

6.1. SchemaBuilder- Dienstprogramm

Die Klasse org.apache.avro.SchemaBuilder ist nützlich zum Erstellen des Schemas .

Lassen Sie uns zunächst das Schema für ClientIdentifier erstellen:

Schema clientIdentifier = SchemaBuilder.record("ClientIdentifier") .namespace("com.baeldung.avro") .fields().requiredString("hostName").requiredString("ipAddress") .endRecord();

Verwenden wir dies nun zum Erstellen eines avroHttpRequest- Schemas:

Schema avroHttpRequest = SchemaBuilder.record("AvroHttpRequest") .namespace("com.baeldung.avro") .fields().requiredLong("requestTime") .name("clientIdentifier") .type(clientIdentifier) .noDefault() .name("employeeNames") .type() .array() .items() .stringType() .arrayDefault(null) .name("active") .type() .enumeration("Active") .symbols("YES","NO") .noDefault() .endRecord();

Hierbei ist zu beachten, dass wir clientIdentifier als Typ für das Feld clientIdentifier zugewiesen haben . In diesem Fall ist clientIdentifier , mit dem der Typ definiert wird, dasselbe Schema, das wir zuvor erstellt haben.

Später können wir die toString- Methode anwenden , um die JSON- Struktur von Schema abzurufen .

Schemadateien werden mit der Erweiterung .avsc gespeichert . Speichern wir unser generiertes Schema in der Datei "src / main / resources / avroHttpRequest-schema.avsc" .

7. Lesen des Schemas

Beim Lesen eines Schemas geht es mehr oder weniger darum , Avro-Klassen für das angegebene Schema zu erstellen . Sobald Avro-Klassen erstellt wurden, können wir sie zum Serialisieren und Deserialisieren von Objekten verwenden.

Es gibt zwei Möglichkeiten, Avro-Klassen zu erstellen:

  • Programmgesteuertes Generieren von Avro-Klassen: Klassen können mit SchemaCompiler generiert werden . Es gibt einige APIs, mit denen wir Java-Klassen generieren können. Wir können den Code für Generierungsklassen auf GitHub finden.
  • Verwenden von Maven zum Generieren von Klassen

Wir haben ein Maven-Plugin, das die Arbeit gut macht. Wir müssen das Plugin einbinden und mvn clean install ausführen .

Fügen wir das Plugin zu unserer Datei pom.xml hinzu :

 org.apache.avro avro-maven-plugin ${avro.version}   schemas generate-sources  schema protocol idl-protocol   ${project.basedir}/src/main/resources/ ${project.basedir}/src/main/java/     

8. Serialisierung und Deserialisierung mit Avro

Nachdem wir mit dem Generieren des Schemas fertig sind, wollen wir den Serialisierungsteil weiter untersuchen.

Avro unterstützt zwei Datenserialisierungsformate: das JSON-Format und das Binärformat.

Zuerst konzentrieren wir uns auf das JSON-Format und dann auf das Binärformat.

Bevor wir fortfahren, sollten wir einige wichtige Schnittstellen durchgehen. Wir können die folgenden Schnittstellen und Klassen für die Serialisierung verwenden:

DatumWriter: We should use this to write data on a given Schema. We'll be using the SpecificDatumWriter implementation in our example, however, DatumWriter has other implementations as well. Other implementations are GenericDatumWriter, Json.Writer, ProtobufDatumWriter, ReflectDatumWriter, ThriftDatumWriter.

Encoder: Encoder is used or defining the format as previously mentioned. EncoderFactory provides two types of encoders, binary encoder, and JSON encoder.

DatumReader: Single interface for de-serialization. Again, it got multiple implementations, but we'll be using SpecificDatumReader in our example. Other implementations are- GenericDatumReader, Json.ObjectReader, Json.Reader, ProtobufDatumReader, ReflectDatumReader, ThriftDatumReader.

Decoder: Decoder is used while de-serializing the data. Decoderfactory provides two types of decoders: binary decoder and JSON decoder.

Next, let's see how serialization and de-serialization happen in Avro.

8.1. Serialization

We'll take the example of AvroHttpRequest class and try to serialize it using Avro.

First of all, let's serialize it in JSON format:

public byte[] serealizeAvroHttpRequestJSON( AvroHttpRequest request) { DatumWriter writer = new SpecificDatumWriter( AvroHttpRequest.class); byte[] data = new byte[0]; ByteArrayOutputStream stream = new ByteArrayOutputStream(); Encoder jsonEncoder = null; try { jsonEncoder = EncoderFactory.get().jsonEncoder( AvroHttpRequest.getClassSchema(), stream); writer.write(request, jsonEncoder); jsonEncoder.flush(); data = stream.toByteArray(); } catch (IOException e) { logger.error("Serialization error:" + e.getMessage()); } return data; } 

Let's have a look at a test case for this method:

@Test public void whenSerialized_UsingJSONEncoder_ObjectGetsSerialized(){ byte[] data = serealizer.serealizeAvroHttpRequestJSON(request); assertTrue(Objects.nonNull(data)); assertTrue(data.length > 0); }

Here we've used the jsonEncoder method and passing the schema to it.

If we wanted to use a binary encoder, we need to replace the jsonEncoder() method with binaryEncoder():

Encoder jsonEncoder = EncoderFactory.get().binaryEncoder(stream,null);

8.2. Deserialization

To do this, we'll be using the above-mentioned DatumReader and Decoder interfaces.

As we used EncoderFactory to get an Encoder, similarly we'll use DecoderFactory to get a Decoder object.

Let's de-serialize the data using JSON format:

public AvroHttpRequest deSerealizeAvroHttpRequestJSON(byte[] data) { DatumReader reader = new SpecificDatumReader(AvroHttpRequest.class); Decoder decoder = null; try { decoder = DecoderFactory.get().jsonDecoder( AvroHttpRequest.getClassSchema(), new String(data)); return reader.read(null, decoder); } catch (IOException e) { logger.error("Deserialization error:" + e.getMessage()); } } 

And let's see the test case:

@Test public void whenDeserializeUsingJSONDecoder_thenActualAndExpectedObjectsAreEqual(){ byte[] data = serealizer.serealizeAvroHttpRequestJSON(request); AvroHttpRequest actualRequest = deSerealizer .deSerealizeAvroHttpRequestJSON(data); assertEquals(actualRequest,request); assertTrue(actualRequest.getRequestTime() .equals(request.getRequestTime())); }

Similarly, we can use a binary decoder:

Decoder decoder = DecoderFactory.get().binaryDecoder(data, null);

9. Conclusion

Apache Avro ist besonders nützlich beim Umgang mit Big Data. Es bietet Datenserialisierung im Binär- und JSON-Format, die je nach Anwendungsfall verwendet werden kann.

Der Avro-Serialisierungsprozess ist schneller und platzsparend. Avro speichert die Feldtypinformationen nicht für jedes Feld. Stattdessen werden Metadaten in einem Schema erstellt.

Last but not least hat Avro eine großartige Bindung mit einer Vielzahl von Programmiersprachen, was ihm einen Vorteil verschafft.

Wie immer ist der Code auf GitHub zu finden.