티스토리 뷰
Download
Avro library Dependency
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro</artifactId>
<version>1.11.0</version>
</dependency>
Avro Plugin
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<sourceDirectory>${project.basedir}/src/main/avro/</sourceDirectory>
<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
Defining a schema
Avro 의 Schema 는 JSON 으로 정의되어있다. Schema 는 Primitive type 과 Complext Type 으로 구성되어있다.
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
이 Schema 는 가상의 User 를 나타내는 Record 를 정의한다. (Schema 파일 하나는 오로지 하나의 Schema 정의만 포함할 수 있다) Record 정의는 최소한 Type, Name, Field 를 포함해야한다. namespace 또한 정의되어있는데, 이것은 name 필드와 합쳐져서 Schema 의 "Full Name" 을 나타내는 요소이다.
필드는 name 과 type 으로 정의되는 객체의 배열로 정의된다. field 의 type 요소는 Schema 객체와는 또 다른 것이고 이는 primitive 타입이거나 Complext 타입이다. 예를들어, User Schema 의 name 필드는 primitive 타입의 string 이고 반면에 favorite_number, favorite_color 필드들은 JSON 배열로 표현되는 union 이다. union 은 list 안에 어떠한 타입으로 맵핑될 수 있는 Complext 타입이다. 따라서 favorite_number 필드는 int 가 될 수도 있고 null 이 될수도 있기 때문에 optional 필드로 만들때 적합하다.
Serializing and deserializing with code generation
Compiling Schema
plugins {
id 'java'
id "com.commercehub.gradle.plugin.avro" version "0.9.1"
}
위와 같이 plugin 을 추가한 후에 Gradle refresh 를 해보면 Avro 관련 Task 들이 추가된것을 확인할 수 있다.
위에서 정의한 Schema 를 User.avsc 파일로 저장한 후에 generateAvroJava Task 를 실행하면 아래와 같이 Class 가 생성되는 것을 확인할 수 있다.
Creating Users
Code Generation 까지 마쳤다면, User 객체를 생성하고 File 로 디스크에 저장한 후에 역직렬화를 통해 User 객체를 불러와보자.
첫번째로 User 객체를 생성하고 각 필드를 세팅한다.
User user1 = new User();
user1.setName("Alyssa");
user1.setFavoriteNumber(256);
// Leave favorite color null
// Alternate constructor
User user2 = new User("Ben", 7, "red");
// Construct via builder
User user3 = User.newBuilder()
.setName("Charlie")
.setFavoriteColor("blue")
.setFavoriteNumber(null)
.build();
예제에서 볼 수 있듯이, Avro 객체는 Builder 를 사용하거나 생성자를 호출해서 생성할 수 있다. 생성자와는 달리 Builder 는 Schema 에 정의되어있는 Default 값을 세팅한다. 게다가 Builder 는 값이 세팅될때 유효성검사를 하는 반면에 생성자를 이용해서 객체를 생성할 경우에는 직렬화가 되기 전까지는 에러가 발생하지 않는다. 그러나 생성자를 사용하는것이 성능이 낫고 Builder 는 이것이 실제로 쓰여지기 전까지는 복사본을 만든다.
user1 에는 favoriteColor 를 지정하지 않은 것을 기억하자. Record 의 타입이 ["string", "null"] 이기 때문에, string 을 지정할 수도 있고 null 로 남겨둘 수도 있는 것이다. 비슷하게 user3 의 favoriteNumber 를 null 로 세팅하였다.
Serializing
위에서 만든 User 객체를 직렬화하여 저장해보자
// Serialize user1, user2 and user3 to disk
DatumWriter<User> userDatumWriter = new SpecificDatumWriter<User>(User.class);
DataFileWriter<User> dataFileWriter = new DataFileWriter<User>(userDatumWriter);
dataFileWriter.create(user1.getSchema(), new File("users.avro"));
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.append(user3);
dataFileWriter.close();
Java 객체를 in-memory 직렬화 포맷으로 변경해주는 DatumWriter 를 생성하였다. SpecificDatumWriter 클래스는 Generated 클래스와 주로 사용되고 특별히 생성된 타입으로부터 Schema 를 추출한다.
다음으로 직렬화된 Record 를 write 하는 역할을 하는 DataFileWriter 를 생성하고 dataFileWriter.create() 를 호출하여 어느 File 에 데이터를 쓸지 특정하였다. dataFileWriter.append() 함수를 호출하여 User 객체를 File 에 썼다. 쓰기 작업이 모두 완료된 후에 Data File 을 닫았다.
Deserializing
위에서 생성한 직렬화 파일을 역직렬화 해보자
// Deserialize Users from disk
DatumReader<User> userDatumReader = new SpecificDatumReader<User>(User.class);
DataFileReader<User> dataFileReader = new DataFileReader<User>(file, userDatumReader);
User user = null;
while (dataFileReader.hasNext()) {
// Reuse user object by passing it to next(). This saves us from
// allocating and garbage collecting many objects for files with
// many items.
user = dataFileReader.next(user);
System.out.println(user);
}
위 예제 코드의 결과는 아래와 같다.
{"name": "Alyssa", "favorite_number": 256, "favorite_color": null}
{"name": "Ben", "favorite_number": 7, "favorite_color": "red"}
{"name": "Charlie", "favorite_number": null, "favorite_color": "blue"}
역직렬화는 직렬화와 비슷하다. 직렬화에 사용한 SpecificDatumWriter 와 비슷한 SpecificDatumReader 를 생성하였다. SpecificDatumReader 는 in-memory 직렬화 데이터를 Generated Class 의 객체로 변환해준다.
DatafileReader 에게 앞서 만들었던 File 객체와 DatumReader 를 전달하였다. DataFilerReader 는 데이터가 쓰여질 당시의 Schema 와 data 모두 불러오게 된다. Data 는 Write 할 당시의 Schema 와 Read 할 당시의 Schema 둘 다 이용하여 불러와지게 된다. 이 예제에서는 User.class 가 Read Schema 로 사용되었다. Write Schema 는 어떤 Field 들이 쓰여졌는지 알아야 하고, 반면에 Read Schema 는 어떤 필드들이 예상되는지, Read Schema 에서 추가된 Field 들의 Default 값은 무엇으로 채워야 하는지를 알야아 한다. 두 Schema 의 차이가 있다면, Schema Resolution 을 통해 해결한다.
다음으로 DataFileReader 를 사용하여 직렬화된 User 객체를 순회하고 역직렬화된 객체를 출력하였다. 기억해야할 것은 Iteration 을 어떻게 수행하였는지이다. 현재 역직렬화된 객체를 저장하기 위해 User 객체를 하나 생성하였고 dataFileReader.next 를 호출할때마다 이 객체를 전달하였다. 이 방법은 DataFileReader 가 동일한 User 객체를 Iteration 할때마다 새로 생성해서 GC 를 일으키지 않도록 하기 위해 동일한 객체를 재활용하도록 최적화한 것이다.
'Avro' 카테고리의 다른 글
Avro - Serializer/Deserializer 비교 (0) | 2022.02.18 |
---|---|
Apache Avro - 설명 (0) | 2022.02.17 |
Apache Avro 소개 (0) | 2022.02.17 |
- Total
- Today
- Yesterday
- HashMap
- ConcurrentHashMap
- Seperate Chaining
- reactor
- ResultSet
- dynamodb
- notify()
- Flux
- mariadb-connector-j
- aurora
- MariaDB
- GlobalFilter
- N+1
- spring cloud gateway
- mariada-connector
- RoutePredication
- reative
- rate limit
- RouteDefinition
- AbstractMethodError
- router
- custom config data convertion
- DyanomoDB
- wait()
- notifyAll()
- msyql-connector-java
- circurit breaker
- referencedColumnName
- getBoolean
- Lazy
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |