1. 概要

データのシリアル化は、データをバイナリ形式またはテキスト形式に変換する手法です。 この目的のために利用可能な複数のシステムがあります。 Apache Avro は、それらのデータシリアル化システムの1つです。

Avroは、言語に依存しないスキーマベースのデータシリアル化ライブラリです。 スキーマを使用して、シリアル化と逆シリアル化を実行します。 さらに、AvroはJSON形式を使用してデータ構造を指定し、より強力にします。

このチュートリアルでは、Avroのセットアップ、シリアル化を実行するためのJava API、およびAvroと他のデータシリアル化システムとの比較について詳しく説明します。

システム全体のベースとなるスキーマの作成に主に焦点を当てます。

2. Apache Avro

Avroは、言語に依存しないシリアル化ライブラリです。 これを行うために、Avroはコアコンポーネントの1つであるスキーマを使用します。 さらにデータ処理するためにスキーマをファイルに保存します

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 は、指定された従業員のリストがアクティブであるかどうかを説明する列挙型です。

私たちの目的は、ApacheAvroを使用してAvroHttRequestクラスをシリアル化および逆シリアル化することです。

4. Avroデータ型

先に進む前に、Avroでサポートされているデータ型について説明しましょう。

Avroは、次の2種類のデータをサポートしています。

  • プリミティブ型:Avroはすべてのプリミティブ型をサポートします。 プリミティブ型名を使用して、特定のフィールドの型を定義します。 たとえば、 String を保持する値は、スキーマで{“ type”:“ string”}として宣言する必要があります
  • 複合型:Avroは、レコード、列挙型、配列、マップ、ユニオン、固定の6種類の複合型をサポートしています。

たとえば、問題ステートメントでは、ClientIdentifierはレコードです。

その場合、ClientIdentifierのスキーマは次のようになります。

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

5. Avroの使用

まず、必要なMaven依存関係をpom.xmlファイルに追加しましょう。

次の依存関係を含める必要があります。

  • Apache Avro –コアコンポーネント
  • コンパイラ–AvroIDLおよびAvro固有のJavaAPIT用のApacheAvroコンパイラ
  • ツール–ApacheAvroコマンドラインツールとユーティリティが含まれます
  • Mavenプロジェクト用のApacheAvroMavenプラグイン

このチュートリアルではバージョン1.8.2を使用しています。

ただし、 MavenCentralで最新バージョンを見つけることを常にお勧めします。

<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スキーマには主に4つの属性があります。

  • Type-は、複雑なタイプかプリミティブ値かを問わず、スキーマのタイプを記述します。
  • 名前空間-は、指定されたスキーマが属する名前空間を記述します
  • Name –スキーマの名前
  • フィールド-は、特定のスキーマに関連付けられているフィールドについて通知します。 フィールドは、プリミティブタイプと複合タイプのどちらでもかまいません。

スキーマを作成する1つの方法は、前のセクションで見たように、JSON表現を作成することです。

SchemaBuilder を使用してスキーマを作成することもできます。これは、間違いなく、より優れた効率的な作成方法です。

6.1. SchemaBuilderユーティリティ

クラスorg.apache.avro.SchemaBuilderは、スキーマの作成に役立ちます。

まず、 ClientIdentifier:のスキーマを作成しましょう。

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

次に、これを使用して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();

ここで重要なのは、clientIdentifierフィールドのタイプとしてclientIdentifierを割り当てたことです。 この場合、タイプの定義に使用される clientIdentifier は、以前に作成したものと同じスキーマです。

後で、 toString メソッドを適用して、SchemaJSON構造を取得できます。

スキーマファイルは、.avsc拡張子を使用して保存されます。 生成したスキーマを「src/main / resources /avroHttpRequest-schema.avsc」ファイルに保存しましょう。

7. スキーマを読む

スキーマの読み取りは、多かれ少なかれ指定されたスキーマのAvroクラスの作成に関するものです。 Avroクラスが作成されると、それらを使用してオブジェクトをシリアル化および逆シリアル化できます。

Avroクラスを作成する方法は2つあります。

  • プログラムによるAvroクラスの生成:クラスはSchemaCompilerを使用して生成できます。 Javaクラスの生成に使用できるAPIがいくつかあります。 生成クラスのコードはGitHubにあります。
  • Mavenを使用してクラスを生成する

うまく機能するMavenプラグインが1つあります。 プラグインを含めて、 mvn cleaninstallを実行する必要があります。

プラグインを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形式とバイナリ形式の2つがあります。

まず、JSON形式に焦点を当て、次にバイナリ形式について説明します。

先に進む前に、いくつかの主要なインターフェースを通過する必要があります。 シリアル化には、以下のインターフェースとクラスを使用できます。

DatumWriter これを使用して、特定のスキーマにデータを書き込む必要があります。 この例ではSpecificDatumWriter実装を使用しますが、DatumWriterには他の実装もあります。 他の実装は、 GenericDatumWriter、Json.Writer、ProtobufDatumWriter、ReflectDatumWriter、ThriftDatumWriterです。

エンコーダー:エンコーダーが使用されるか、前述のようにフォーマットを定義します。 EncoderFactory は、バイナリエンコーダーとJSONエンコーダーの2種類のエンコーダーを提供します。

DatumReader 逆シリアル化のための単一のインターフェース。 ここでも、複数の実装がありますが、この例ではSpecificDatumReaderを使用します。 その他の実装は次のとおりです-GenericDatumReader、Json.ObjectReader、Json.Reader、ProtobufDatumReader、ReflectDatumReader、ThriftDatumReader。

デコーダー:データの逆シリアル化中にデコーダーが使用されます。 Decoderfactory は、バイナリデコーダーとJSONデコーダーの2種類のデコーダーを提供します。

次に、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にあります。