Guia para Apache Avro

Guia para Apache Avro

1. Visão geral

A serialização de dados é uma técnica de conversão de dados em formato binário ou de texto. Existem vários sistemas disponíveis para esse fim. Apache Avro é um desses sistemas de serialização de dados.

Avro is a language independent, schema-based data serialization library. Ele usa um esquema para executar serialização e desserialização. Além disso, o Avro usa um formato JSON para especificar a estrutura de dados que o torna mais poderoso.

Neste tutorial, vamos explorar mais sobre a configuração do Avro, a API Java para realizar a serialização e uma comparação do Avro com outros sistemas de serialização de dados.

Vamos nos concentrar principalmente na criação de esquemas, que é a base de todo o sistema.

2. Apache Avro

Avro é uma biblioteca de serialização independente de idioma. Para fazer isso, o Avro usa um esquema que é um dos componentes principais. Éstores the schema in a file for further data processing.

O Avro é o mais adequado para o processamento de Big Data. É muito popular no mundo Hadoop e Kafka por seu processamento mais rápido.

O Avro cria um arquivo de dados onde mantém os dados junto com o esquema em sua seção de metadados. Acima de tudo, ele fornece uma estrutura de dados rica que o torna mais popular do que outras soluções semelhantes.

Para usar o Avro para serialização, precisamos seguir as etapas mencionadas abaixo.

3. Declaração do Problema

Vamos começar definindo uma classe chamadaAvroHttRequest que usaremos em nossos exemplos. A classe contém atributos de tipo primitivo e complexo:

class AvroHttpRequest {

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

Aqui,requestTime é um valor primitivo. ClientIdentifier é outra classe que representa um tipo complexo. Também temosemployeeName, que é novamente um tipo complexo. Active é um enum para descrever se a lista de funcionários fornecida está ativa ou não.

Nosso objetivo é serializar e desserializar a classeAvroHttRequest usando Apache Avro.

4. Tipos de dados Avro

Antes de prosseguir, vamos discutir os tipos de dados suportados pelo Avro.

O Avro suporta dois tipos de dados:

  • Tipo primitivo: O Avro suporta todos os tipos primitivos. Usamos o nome do tipo primitivo para definir um tipo de um determinado campo. Por exemplo, um valor que contém umString deve ser declarado como \ {“tipo”: “string”} no Esquema

  • Tipo complexo: O Avro suporta seis tipos de tipos complexos: registros, enumerações, matrizes, mapas, uniões e unidades fixas.

Por exemplo, em nossa declaração de problema,ClientIdentifier é um registro.

Nesse caso, o esquema paraClientIdentifier deve ser semelhante a:

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

5. Usando o Avro

Para começar, vamos adicionar as dependências do Maven que precisaremos ao nosso arquivopom.xml.

Devemos incluir as seguintes dependências:

  • Apache Avro - componentes principais

  • Compiler - Compiladores Apache Avro para Avro IDL e Avro Java APIT específico

  • Ferramentas - que inclui ferramentas e utilitários de linha de comando Apache Avro

  • Apache Avro Maven Plugin para projetos Maven

Estamos usando a versão 1.8.2 para este tutorial.

No entanto, é sempre aconselhável encontrar a versão mais recente emMaven Central:


    org.apache.avro
    avro-compiler
    1.8.2


    org.apache.avro
    avro-maven-plugin
    1.8.2

Depois de adicionar as dependências do maven, as próximas etapas serão:

  • Criação de esquema

  • Lendo o esquema em nosso programa

  • Serializando nossos dados usando o Avro

  • Por fim, desserialize os dados

6. Criação de esquema

A Avro descreve seu esquema usando um formato JSON. Existem principalmente quatro atributos para um determinado esquema do Avro:

  • Type- que descreve o tipo de esquema, seja seu tipo complexo ou valor primitivo

  • Namespace- que descreve o namespace ao qual o determinado esquema pertence

  • Name - o nome do Esquema

  • Fields- que informa sobre os campos associados a um determinado esquema. Fields can be of primitive as well as complex type.

Uma maneira de criar o esquema é escrever a representação JSON, como vimos nas seções anteriores.

Também podemos criar um esquema usandoSchemaBuilder, que é inegavelmente uma maneira melhor e eficiente de criá-lo.

6.1. UtilidadeSchemaBuilder

A classeorg.apache.avro.SchemaBuilder é útil para criar o Schema.

Em primeiro lugar, vamos criar o esquema paraClientIdentifier:

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

Agora, vamos usar isso para criar um esquemaavroHttpRequest:

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

É importante notar aqui que atribuímosclientIdentifier como o tipo para o campoclientIdentifier. Nesse caso,clientIdentifier usado para definir o tipo é o mesmo esquema que criamos antes.

Mais tarde, podemos aplicar o métodotoString para obter a estruturaJSON deSchema.

Schema files are saved using the .avsc extension. Vamos salvar nosso esquema gerado no arquivo“src/main/resources/avroHttpRequest-schema.avsc”.

7. Lendo o esquema

Ler um esquema é mais ou menos cerca decreating Avro classes for the given schema. Depois que as classes Avro são criadas, podemos usá-las para serializar e desserializar objetos.

Existem duas maneiras de criar classes Avro:

  • Gerando classes Avro programaticamente: as classes podem ser geradas usandoSchemaCompiler. Existem algumas APIs que podemos usar para gerar classes Java. Podemos encontrar o código para as classes de geração no GitHub.

  • Usando Maven para gerar classes

Nós temos um plugin maven que faz o trabalho bem. Precisamos incluir o plugin e executarmvn clean install.

Vamos adicionar o plugin ao nosso arquivopom.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. Serialização e desserialização com Avro

Quando terminarmos de gerar o esquema, vamos continuar explorando a parte de serialização.

Existem dois formatos de serialização de dados suportados pelo Avro: formato JSON e formato binário.

Primeiro, vamos nos concentrar no formato JSON e, em seguida, vamos discutir o formato Binário.

Antes de prosseguir, devemos passar por algumas interfaces principais. Podemos usar as interfaces e classes abaixo para serialização:

DatumWriter<T>: Devemos usar isso para escrever dados em um determinado esquema. Estaremos usando a implementaçãoSpecificDatumWriter em nosso exemplo, no entanto,DatumWriter tem outras implementações também. Outras implementações sãoGenericDatumWriter, Json.Writer, ProtobufDatumWriter, ReflectDatumWriter, ThriftDatumWriter.

Encoder: O codificador é usado ou definindo o formato conforme mencionado anteriormente. EncoderFactory fornece dois tipos de codificadores, codificador binário e codificador JSON.

DatumReader<D>: Interface única para desserialização. Novamente, ele tem várias implementações, mas usaremosSpecificDatumReader em nosso exemplo. Outras implementações são-GenericDatumReader, Json.ObjectReader, Json.Reader, ProtobufDatumReader, ReflectDatumReader, ThriftDatumReader.

Decoder: O decodificador é usado durante a desserialização dos dados. Decoderfactory fornece dois tipos de decodificadores: decodificador binário e decodificador JSON.

A seguir, vamos ver como a serialização e a desserialização acontecem no Avro.

8.1. Serialização

Vamos pegar o exemplo da classeAvroHttpRequest e tentar serializá-la usando Avro.

Em primeiro lugar, vamos serializá-lo no formato JSON:

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

Vamos dar uma olhada em um caso de teste para este método:

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

Aqui, usamos o métodojsonEncoder e passamos o esquema para ele.

Se quisermos usar um codificador binário, precisamos substituir o métodojsonEncoder() porbinaryEncoder():

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

8.2. Desserialização

Para fazer isso, usaremos as interfacesDatumReader eDecoder mencionadas acima.

Como usamosEncoderFactory para obter umEncoder,, usaremosDecoderFactory para obter um objetoDecoder.

Vamos desserializar os dados usando o formato JSON:

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

E vamos ver o caso de teste:

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

Da mesma forma, podemos usar um decodificador binário:

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

9. Conclusão

O Apache Avro é especialmente útil ao lidar com big data. Ele oferece serialização de dados no formato binário e JSON, que pode ser usado conforme o caso de uso.

O processo de serialização do Avro é mais rápido e também economiza espaço. A Avro não mantém as informações do tipo de campo em cada campo; em vez disso, ele cria metadados em um esquema.

Por último, mas não menos importante, o Avro tem uma ótima ligação com uma ampla variedade de linguagens de programação, o que lhe dá uma vantagem.

Como sempre, o código pode ser encontradoover on GitHub.