Перейти к основному содержимому

Руководство по Apache Avro

· 7 мин. чтения

1. Обзор

Сериализация данных — это метод преобразования данных в двоичный или текстовый формат. Для этой цели существует несколько систем. Apache Avro — одна из таких систем сериализации данных.

Avro — это независимая от языка библиотека сериализации данных на основе схемы . Он использует схему для выполнения сериализации и десериализации. Кроме того, Avro использует формат JSON для указания структуры данных, что делает его более мощным.

В этом руководстве мы больше узнаем о настройке Avro, API Java для выполнения сериализации и сравнении Avro с другими системами сериализации данных.

Мы сосредоточимся в первую очередь на создании схемы, которая является основой всей системы.

2. Апач Авро

Avro — это независимая от языка библиотека сериализации. Для этого Avro использует схему, которая является одним из основных компонентов. Он сохраняет схему в файле для дальнейшей обработки данных .

Avro лучше всего подходит для обработки больших данных. Он довольно популярен в мире Hadoop и Kafka из-за более быстрой обработки.

Avro создает файл данных, в котором он хранит данные вместе со схемой в своем разделе метаданных. Прежде всего, он обеспечивает богатую структуру данных, что делает его более популярным, чем другие подобные решения.

Чтобы использовать Avro для сериализации, нам нужно выполнить шаги, указанные ниже.

3. Постановка задачи

Начнем с определения класса AvroHttRequest , который мы будем использовать в наших примерах. Класс содержит как примитивные, так и сложные атрибуты типа:

class AvroHttpRequest {

private long requestTime;
private ClientIdentifier clientIdentifier;
private List<String> employeeNames;
private Active active;
}

Здесь requestTime является примитивным значением. ClientIdentifier — это еще один класс, представляющий сложный тип. У нас также есть employeeName , который снова является сложным типом. Active — это перечисление, описывающее, является ли данный список сотрудников активным или нет.

Наша цель — сериализовать и десериализовать класс AvroHttRequest с помощью Apache Avro.

4. Типы данных Avro

Прежде чем двигаться дальше, давайте обсудим типы данных, поддерживаемые Avro.

Avro поддерживает два типа данных:

  • Примитивный тип: Avro поддерживает все примитивные типы. Мы используем имя примитивного типа для определения типа данного поля. Например, значение, которое содержит строку , должно быть объявлено как {"type": "string"} в схеме.
  • Сложный тип: Avro поддерживает шесть видов сложных типов: записи, перечисления, массивы, карты, объединения и фиксированные типы.

Например, в нашей постановке задачи ClientIdentifier — это запись.

В этом случае схема для ClientIdentifier должна выглядеть так:

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

5. Использование Авро

Для начала давайте добавим зависимости Maven, которые нам понадобятся, в наш файл pom.xml .

Мы должны включить следующие зависимости:

  • Apache Avro — основные компоненты
  • Компилятор — компиляторы Apache Avro для Avro IDL и Java APIT, специфичный для Avro
  • Инструменты — в том числе инструменты и утилиты командной строки Apache Avro.
  • Плагин Apache Avro Maven для проектов Maven

В этом руководстве мы используем версию 1.8.2.

Однако всегда рекомендуется найти последнюю версию на Maven Central :

<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-compiler</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.8.2</version>
</dependency>

После добавления зависимостей maven следующими шагами будут:

  • Создание схемы
  • Чтение схемы в нашей программе
  • Сериализация наших данных с помощью Avro
  • Наконец, десериализуйте данные

6. Создание схемы

Avro описывает свою схему в формате JSON. В основном существует четыре атрибута для данной схемы Avro:

  • Тип - который описывает тип схемы, будь то ее сложный тип или примитивное значение.
  • Пространство имен - описывает пространство имен, к которому принадлежит данная схема.
  • Имя – название схемы
  • Поля — в котором рассказывается о полях, связанных с данной схемой. Поля могут быть как примитивного, так и сложного типа .

Один из способов создания схемы — написать представление JSON, как мы видели в предыдущих разделах.

Мы также можем создать схему с помощью SchemaBuilder , что, несомненно, является лучшим и эффективным способом ее создания.

6.1. Утилита SchemaBuilder

Класс org.apache.avro.SchemaBuilder полезен для создания схемы.

Прежде всего, давайте создадим схему для ClientIdentifier:

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

Теперь давайте воспользуемся этим для создания схемы avroHttpRequest :

Schema avroHttpRequest = SchemaBuilder.record("AvroHttpRequest")
.namespace("com.foreach.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();

Здесь важно отметить, что мы назначили clientIdentifier в качестве типа для поля clientIdentifier . В этом случае clientIdentifier , используемый для определения типа, является той же схемой, которую мы создали ранее.

Позже мы можем применить метод toString , чтобы получить JSON - структуру Schema .

Файлы схемы сохраняются с расширением .avsc . Давайте сохраним нашу сгенерированную схему в файл «src/main/resources/avroHttpRequest-schema.avsc» .

7. Чтение схемы

Чтение схемы в большей или меньшей степени связано с созданием классов Avro для данной схемы . После создания классов Avro мы можем использовать их для сериализации и десериализации объектов.

Существует два способа создания классов Avro:

  • Программное создание классов Avro: классы можно создавать с помощью SchemaCompiler . Есть несколько API, которые мы можем использовать для создания классов Java. Мы можем найти код для классов генерации на GitHub.
  • Использование Maven для создания классов

У нас есть один плагин maven, который хорошо справляется со своей задачей. Нам нужно включить плагин и запустить mvn clean install .

Давайте добавим плагин в наш файл pom.xml :

<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>${avro.version}</version>
<executions>
<execution>
<id>schemas</id>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
<goal>protocol</goal>
<goal>idl-protocol</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/resources/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

8. Сериализация и десериализация с Avro

Поскольку мы закончили создание схемы, давайте продолжим изучение части сериализации.

Avro поддерживает два формата сериализации данных: формат JSON и двоичный формат.

Сначала мы сосредоточимся на формате JSON, а затем обсудим двоичный формат.

Прежде чем двигаться дальше, мы должны пройти через несколько ключевых интерфейсов. Мы можем использовать интерфейсы и классы ниже для сериализации:

DatumWriter<T>: мы должны использовать это для записи данных в данной схеме. В нашем примере мы будем использовать реализацию SpecificDatumWriter , однако у DatumWriter есть и другие реализации. Другими реализациями являются GenericDatumWriter, Json.Writer, ProtobufDatumWriter, ReflectDatumWriter, ThriftDatumWriter.

Кодировщик : используется кодировщик или определяется формат, как упоминалось ранее. EncoderFactory предоставляет два типа кодировщиков: двоичный кодировщик и кодировщик JSON.

DatumReader<D>: единый интерфейс для десериализации. Опять же, у него есть несколько реализаций, но в нашем примере мы будем использовать SpecificDatumReader . Другие реализации : GenericDatumReader, Json.ObjectReader, Json.Reader, ProtobufDatumReader, ReflectDatumReader, ThriftDatumReader.

Декодер: декодер используется при десериализации данных. Decoderfactory предоставляет два типа декодеров: двоичный декодер и декодер JSON.

Далее давайте посмотрим, как сериализация и десериализация происходят в Avro.

8.1. Сериализация

Мы возьмем пример класса AvroHttpRequest и попытаемся сериализовать его с помощью Avro.

Прежде всего, давайте сериализуем его в формате JSON:

public byte[] serealizeAvroHttpRequestJSON(
AvroHttpRequest request) {

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

Давайте посмотрим на тестовый пример для этого метода:

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

Здесь мы использовали метод jsonEncoder и передали ему схему.

Если мы хотим использовать двоичный кодировщик, нам нужно заменить метод jsonEncoder() на binaryEncoder():

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

8.2. Десериализация

Для этого мы будем использовать вышеупомянутые интерфейсы DatumReader и Decoder .

Как мы использовали EncoderFactory для получения Encoder, так же мы будем использовать DecoderFactory для получения объекта Decoder .

Давайте десериализуем данные в формате JSON:

public AvroHttpRequest deSerealizeAvroHttpRequestJSON(byte[] data) {
DatumReader<AvroHttpRequest> 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());
}
}

И давайте посмотрим тестовый пример:

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

Точно так же мы можем использовать двоичный декодер:

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

9. Заключение

Apache Avro особенно полезен при работе с большими данными. Он предлагает сериализацию данных в двоичном формате, а также в формате JSON, который можно использовать в соответствии с вариантом использования.

Процесс сериализации Avro быстрее, а также занимает меньше места. Avro не сохраняет информацию о типе поля для каждого поля; вместо этого он создает метаданные в схеме.

И последнее, но не менее важное: у Avro отличная привязка к широкому спектру языков программирования, что дает ему преимущество.

Как всегда, код можно найти на GitHub .