Przewodnik po Apache Avro

1. Przegląd

Serializacja danych to technika konwersji danych do formatu binarnego lub tekstowego. Dostępnych jest wiele systemów do tego celu. Apache Avro jest jednym z tych systemów serializacji danych.

Avro to niezależna od języka, oparta na schemacie biblioteka serializacji danych . Używa schematu do wykonywania serializacji i deserializacji. Ponadto Avro używa formatu JSON do określenia struktury danych, dzięki czemu jest bardziej wydajny.

W tym samouczku omówimy więcej na temat konfiguracji Avro, interfejsu API języka Java do wykonywania serializacji oraz porównania Avro z innymi systemami serializacji danych.

Skoncentrujemy się przede wszystkim na tworzeniu schematu, który jest podstawą całego systemu.

2. Apache Avro

Avro to niezależna od języka biblioteka serializacji. Aby to zrobić, Avro używa schematu, który jest jednym z podstawowych komponentów. To przechowuje schemat w pliku do dalszego przetwarzania danych .

Avro najlepiej nadaje się do przetwarzania Big Data. Jest dość popularny w świecie Hadoop i Kafka ze względu na szybsze przetwarzanie.

Avro tworzy plik danych, w którym przechowuje dane wraz ze schematem w sekcji metadanych. Przede wszystkim zapewnia bogatą strukturę danych, dzięki czemu jest bardziej popularny niż inne podobne rozwiązania.

Aby użyć Avro do serializacji, musimy wykonać kroki wymienione poniżej.

3. Opis problemu

Zacznijmy od zdefiniowania klasy o nazwie AvroHttRequest , której użyjemy w naszych przykładach. Klasa zawiera prymitywne i złożone atrybuty typu:

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

W tym przypadku requestTime jest wartością pierwotną. ClientIdentifier to kolejna klasa, która reprezentuje typ złożony. Mamy również EmployerName, która jest ponownie złożonym typem. Aktywny to wyliczenie opisujące, czy dana lista pracowników jest aktywna, czy nie.

Naszym celem jest serializacja i deserializacja klasy AvroHttRequest przy użyciu Apache Avro.

4. Typy danych Avro

Zanim przejdziemy dalej, omówmy typy danych obsługiwane przez Avro.

Avro obsługuje dwa typy danych:

  • Typ prymitywny: Avro obsługuje wszystkie typy pierwotne. Używamy pierwotnej nazwy typu do zdefiniowania typu danego pola. Na przykład wartość przechowująca ciąg znaków powinna zostać zadeklarowana jako {„type”: „string”} w schemacie
  • Typ złożony: Avro obsługuje sześć rodzajów typów złożonych: rekordy, wyliczenia, tablice, mapy, sumy i ustalone

Na przykład w naszym opisie problemu ClientIdentifier jest rekordem.

W takim przypadku schemat ClientIdentifier powinien wyglądać następująco:

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

5. Korzystanie z Avro

Na początek dodajmy zależności Mavena, których będziemy potrzebować, do naszego pliku pom.xml .

Powinniśmy uwzględnić następujące zależności:

  • Apache Avro - podstawowe komponenty
  • Kompilator - Apache Avro Compilers for Avro IDL i Avro Specific Java APIT
  • Narzędzia - w tym narzędzia i narzędzia wiersza poleceń Apache Avro
  • Wtyczka Apache Avro Maven do projektów Maven

W tym samouczku używamy wersji 1.8.2.

Jednak zawsze zaleca się znalezienie najnowszej wersji w Maven Central:

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

Po dodaniu zależności maven, kolejne kroki będą następujące:

  • Tworzenie schematu
  • Czytanie schematu w naszym programie
  • Serializacja naszych danych za pomocą Avro
  • Na koniec zdeserializuj dane

6. Tworzenie schematu

Avro opisuje swój schemat przy użyciu formatu JSON. Istnieją cztery atrybuty danego schematu Avro:

  • Typu który opisuje typ schematu czy jego typu złożonego lub wartości pierwotnej
  • Przestrzeń nazw - która opisuje przestrzeń nazw, do której należy dany schemat
  • Nazwa - nazwa schematu
  • Fields , która opowiada o dziedzinach związanych z danym schemacie. Pola mogą być zarówno typu pierwotnego, jak i złożonego .

Jednym ze sposobów tworzenia schematu jest zapisanie reprezentacji JSON, jak widzieliśmy w poprzednich sekcjach.

Możemy również stworzyć schemat za pomocą SchemaBuilder, co jest niezaprzeczalnie lepszym i wydajniejszym sposobem jego tworzenia.

6.1. Narzędzie SchemaBuilder

Klasa org.apache.avro.SchemaBuilder jest przydatna do tworzenia schematu.

Przede wszystkim stwórzmy schemat dla ClientIdentifier:

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

Teraz użyjmy tego do stworzenia schematu avroHttpRequest :

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

Należy tutaj zauważyć, że przypisaliśmy clientIdentifier jako typ pola clientIdentifier . W tym przypadku clientIdentifier używany do definiowania typu jest tym samym schematem, który utworzyliśmy wcześniej.

Później możemy zastosować metodę toString , aby uzyskać strukturę JSON Schema .

Pliki schematów są zapisywane z rozszerzeniem .avsc . Zapiszmy nasz wygenerowany schemat do pliku „src / main / resources / avroHttpRequest-schema.avsc” .

7. Czytanie schematu

Czytanie schematu polega mniej więcej na tworzeniu klas Avro dla danego schematu . Po utworzeniu klas Avro możemy ich użyć do serializacji i deserializacji obiektów.

Istnieją dwa sposoby tworzenia klas Avro:

  • Programowe generowanie klas Avro: Klasy można generować za pomocą SchemaCompiler . Istnieje kilka interfejsów API, których możemy użyć do generowania klas Java. Kod do generowania klas możemy znaleźć w serwisie GitHub.
  • Używanie Mavena do generowania klas

Mamy jedną wtyczkę Maven, która dobrze sobie radzi. Musimy dołączyć wtyczkę i uruchomić czystą instalację mvn .

Dodajmy wtyczkę do naszego pliku pom.xml :

 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. Serializacja i deserializacja z Avro

Po zakończeniu generowania schematu kontynuujmy eksplorację części serializacji.

Istnieją dwa formaty serializacji danych, które Avro obsługuje: format JSON i format binarny.

Najpierw skupimy się na formacie JSON, a następnie omówimy format binarny.

Zanim przejdziemy dalej, powinniśmy przejść przez kilka kluczowych interfejsów. Do serializacji możemy użyć poniższych interfejsów i klas:

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 jest szczególnie przydatny w przypadku dużych zbiorów danych. Oferuje serializację danych w formacie binarnym, a także w formacie JSON, które mogą być używane zgodnie z przypadkiem użycia.

Proces serializacji Avro jest szybszy, a także zajmuje mało miejsca. Avro nie przechowuje informacji o typie pola w każdym polu; zamiast tego tworzy metadane w schemacie.

Wreszcie, Avro ma świetne powiązanie z szeroką gamą języków programowania, co daje mu przewagę.

Jak zawsze kod można znaleźć na GitHub.