Anleitung zum Apache Avro

Anleitung zu Apache Avro

1. Überblick

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 is a language independent, schema-based data serialization library. Es verwendet ein Schema, um die 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. Esstores the schema in a file for further data processing.

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 die Daten zusammen mit dem Schema im Metadatenabschnitt gespeichert werden. Vor allem bietet es eine reichhaltige Datenstruktur, die es beliebter macht als andere ähnliche Lösungen.

Um Avro für die Serialisierung zu verwenden, müssen Sie die folgenden Schritte ausführen.

3. Problemstellung

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

class AvroHttpRequest {

    private long requestTime;
    private ClientIdentifier clientIdentifier;
    private List employeeNames;
    private Active active;
}

Hier istrequestTime ein primitiver Wert. ClientIdentifier ist eine andere Klasse, die einen komplexen Typ darstellt. Wir haben auchemployeeName, was wiederum ein komplexer Typ ist. Active ist eine Aufzählung, um zu beschreiben, ob die angegebene Liste der Mitarbeiter aktiv ist oder nicht.

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

4. Avro-Datentypen

Bevor wir fortfahren, wollen wir die von Avro unterstützten Datentypen erläutern.

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. Beispielsweise sollte ein Wert, derString enthält, im Schema als \ {"Typ": "Zeichenfolge"} deklariert werden

  • Komplexer Typ: Avro unterstützt sechs Arten von komplexen Typen: Datensätze, Aufzählungen, Arrays, Karten, Gewerkschaften und feste

In unserer Problemstellung ist beispielsweiseClientIdentifier ein Datensatz.

In diesem Fall sollte das Schema fürClientIdentifier wie folgt aussehen:

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

5. Avro verwenden

Fügen wir zunächst die benötigten Maven-Abhängigkeiten zu unsererpom.xml-Datei 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 - Enthält Apache Avro-Befehlszeilentools und -Dienstprogramme

  • Apache Avro Maven Plugin für Maven Projekte

Für dieses Tutorial verwenden wir Version 1.8.2.

Es wird jedoch immer empfohlen, die neueste Version aufMaven 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 deserialisieren Sie die Daten

6. Schema-Erstellung

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

  • Type-, das den Typ des Schemas beschreibt, unabhängig davon, ob es sich um einen komplexen Typ oder einen primitiven Wert handelt

  • Namespace-, das den Namespace beschreibt, zu dem das angegebene Schema gehört

  • Name - der Name des Schemas

  • Fields- gibt Auskunft über die Felder, die einem bestimmten Schema zugeordnet sind. Fields can be of primitive as well as complex type.

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 mitSchemaBuilder erstellen, was zweifellos eine bessere und effizientere Möglichkeit ist, es zu erstellen.

6.1. SchemaBuilder Dienstprogramm

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

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

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

Verwenden Sie dies nun zum Erstellen einesavroHttpRequest-Schemas:

Schema avroHttpRequest = SchemaBuilder.record("AvroHttpRequest")
  .namespace("com.example.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 wirclientIdentifier als Typ für das FeldclientIdentifier zugewiesen haben. In diesem Fall istclientIdentifier, das zum Definieren des Typs verwendet wird, dasselbe Schema, das wir zuvor erstellt haben.

Später können wir dietoString-Methode anwenden, um dieJSON-Struktur vonSchema zu erhalten.

Schema files are saved using the .avsc extension. Speichern wir unser generiertes Schema in der Datei“src/main/resources/avroHttpRequest-schema.avsc”.

7. Schema lesen

Das Lesen eines Schemas entspricht mehr oder wenigercreating Avro classes for the given schema. Sobald Avro-Klassen erstellt sind, 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 mitSchemaCompiler generiert werden. Es gibt einige APIs, die wir zum Generieren von Java-Klassen verwenden 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 den Job gut macht. Wir müssen das Plugin einbinden undmvn clean install ausführen.

Fügen wir das Plugin zu unsererpom.xml-Datei 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, werden wir den Serialisierungsteil weiter untersuchen.

Avro unterstützt zwei Daten-Serialisierungsformate: 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<T>: Wir sollten dies verwenden, um Daten in ein bestimmtes Schema zu schreiben. In unserem Beispiel wird die Implementierung vonSpecificDatumWriterverwendet.DatumWriterhat jedoch auch andere Implementierungen. Andere Implementierungen sindGenericDatumWriter, Json.Writer, ProtobufDatumWriter, ReflectDatumWriter, ThriftDatumWriter.

Encoder: Der Encoder wird verwendet oder definiert das Format wie zuvor erwähnt. EncoderFactory bietet zwei Arten von Codierern: Binärcodierer und JSON-Codierer.

DatumReader<D>: Einzelschnittstelle zur De-Serialisierung. Auch hier wurden mehrere Implementierungen vorgenommen, in unserem Beispiel werden jedochSpecificDatumReader verwendet. Andere Implementierungen sindGenericDatumReader, Json.ObjectReader, Json.Reader, ProtobufDatumReader, ReflectDatumReader, ThriftDatumReader.

Decoder: Der Decoder wird beim De-Serialisieren der Daten verwendet. Decoderfactory bietet zwei Arten von Decodern: Binärdecoder und JSON-Decoder.

Als nächstes wollen wir sehen, wie Serialisierung und De-Serialisierung in Avro stattfinden.

8.1. Serialisierung

Wir nehmen das Beispiel der KlasseAvroHttpRequestund versuchen, sie mit Avro zu serialisieren.

Lassen Sie es uns zunächst im JSON-Format serialisieren:

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;
}

Schauen wir uns einen Testfall für diese Methode an:

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

Hier haben wir diejsonEncoder-Methode verwendet und das Schema an sie übergeben.

Wenn wir einen Binärcodierer verwenden möchten, müssen wir diejsonEncoder()-Methode durchbinaryEncoder(): ersetzen

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

8.2. Deserialisierung

Dazu verwenden wir die oben genannten SchnittstellenDatumReader undDecoder.

Da wirEncoderFactory verwendet haben, um einEncoder, zu erhalten, werden wirDecoderFactory verwenden, um einDecoder-Objekt zu erhalten.

Lassen Sie uns die Daten im JSON-Format de-serialisieren:

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());
    }
}

Und sehen wir uns den Testfall an:

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

Ebenso können wir einen Binärdecoder verwenden:

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

9. Fazit

Apache Avro ist besonders nützlich, wenn Sie mit Big Data arbeiten. Es bietet Datenserialisierung sowohl im Binär- als auch im JSON-Format, die je nach Anwendungsfall verwendet werden können.

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

Zu guter Letzt hat Avro eine großartige Bindung mit einer Vielzahl von Programmiersprachen, was ihm einen Vorteil verschafft.

Wie immer kann der Codeover on GitHub gefunden werden.