Apache Avro를 이용한 Kafka Message Schema 관리(1)
Apache Avro란?
Apache Avro™ is a data serialization system
- Apache Avro™ 1.11.0 Documentation
Apache Avro는 데이터 직렬화에 관한 내용을 포괄하는 시스템이다.
다만 우리가 실제로 사용할 때는 이렇게 거창하게 생각할 필요는 없고 아래의 세 가지 기능을 제공하는 시스템이라고 생각하면 편하다. (실제 공식문서를 봐도 각 언어별로 사용하는 법 중심으로 간결하게 정리되어 있다)
1. 메시지 스키마를 제공해 준다
아래와 같이 Avro 스키마를 JSON으로 쉽게 정의할 수 있다. 파일의 확장자는 .avsc 로 저장하면 된다
{"namespace": "example.avro",
"type": "record",
"name": "User",
"fields": [
{"name": "name", "type": "string"},
{"name": "favorite_number", "type": ["int", "null"]},
{"name": "favorite_color", "type": ["string", "null"]}
]
}
2. 해당 스키마에 맞게 메시지를 직렬화/역직렬화 해준다
위와 같이 정의된 스키마를 읽어와, 직렬화/역직렬화에 사용할 수 있다
// 1. Schema를 불러온다
Schema schema = new Schema.Parser().parse(new File("user.avsc"));
// 2. Schema에 맞게 User를 생성한다
GenericRecord user1 = new GenericData.Record(schema);
user1.put("name", "Alyssa");
user1.put("favorite_number", 256);
// Leave favorite color null
GenericRecord user2 = new GenericData.Record(schema);
user2.put("name", "Ben");
user2.put("favorite_number", 7);
user2.put("favorite_color", "red");
// 3. 직렬화/역직렬화에 사용한다
// Serialize user1 and user2 to disk
File file = new File("users.avro");
DatumWriter<GenericRecord> datumWriter = new GenericDatumWriter<GenericRecord>(schema);
DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<GenericRecord>(datumWriter);
dataFileWriter.create(schema, file);
dataFileWriter.append(user1);
dataFileWriter.append(user2);
dataFileWriter.close();
// Deserialize users from disk
DatumReader<GenericRecord> datumReader = new GenericDatumReader<GenericRecord>(schema);
DataFileReader<GenericRecord> dataFileReader = new DataFileReader<GenericRecord>(file, datumReader);
GenericRecord 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);
3. (필요하다면) 스키마를 소스코드로 만들어 준다(Code Generation)
avro-tools-1.11.0.jar 파일을 가지고 있다면, 아래와 같이 정의된 스키마를 Java 코드로 변환할 수 있다
java -jar /path/to/avro-tools-1.11.0.jar compile schema user.avsc .
생성된 User 클래스는 아래와 같다. 앞뒤로 수많은 Boilerplate Code를 생략하고 보면, 우리가 Schema 파일에 정의한 필드들을 가지고 있음을 확인할 수 있다.
/**
* Autogenerated by Avro
*
* DO NOT EDIT DIRECTLY
*/
package example.avro;
import org.apache.avro.generic.GenericArray;
import org.apache.avro.specific.SpecificData;
import org.apache.avro.util.Utf8;
import org.apache.avro.message.BinaryMessageEncoder;
import org.apache.avro.message.BinaryMessageDecoder;
import org.apache.avro.message.SchemaStore;
@org.apache.avro.specific.AvroGenerated
public class User extends org.apache.avro.specific.SpecificRecordBase implements org.apache.avro.specific.SpecificRecord {
private static final long serialVersionUID = -3588479540582100558L;
// ... 생략
// 스키마에 정의된 필드들
private java.lang.CharSequence name;
private java.lang.Integer favorite_number;
private java.lang.CharSequence favorite_color;
// ... 생략
}
Avro를 통한 Kafka Message Schema 관리
이런 Avro의 대표적인 사용례는 바로 Producer와 Consumer 간의 Message Schema 관리이다. 그럴 수 밖에 없는 것이, Producer와 Consumer가 동일한 스키마 정보를 가지고 있어야 메시지를 주고받는데에 문제가 없을 것이고 Avro의 핵심이 바로 통일된 스키마를 제공해 주는 것이기 때문이다.
그렇다면, Producer와 Consumer간에 어떻게 동일한 스키마를 공유할 수 있을까? 다양한 방법이 있을 수 있겠지만, 조사해 본 결과 일반적으로 아래의 두 가지 방법이 사용되는 것 같다.
1. 별도의 Schema Registry Server에서 스키마를 제공
오직 스키마만 관리하는 별도의 Schema Registry Server를 두고 Producer와 Consumer는 해당 서버로부터 스키마 정보를 받아 사용하는 방식이다.
장점 : 스키마 변경이 일어나도, 실시간으로 Registry에 반영되므로 문제가 없다
단점 : Schema Registry Server 구축 및 관리의 어려움. API 요청 증가로 인한 부하 발생
2. Git Submodule을 이용해 Schema Repository를 공유

스키마를 공통의 Repository에 저장해 두고, Producer와 Consumer가 공통으로 해당 Repository를 Submodule로 가지고 있는 방식이다. (위의 사진의 .py가 .avsc 로 바뀐다고 생각하면 된다)
장점 : Producer와 Consumer 입장에서는 개발이 매우 간단하다 (avsc파일을 그냥 가져다 쓰면 된다)
단점 : Git Submodule에 대한 이해와 관리가 필요. 실시간으로 일어나는 스키마 변경에는 대응하지 못한다 (Producer와 Consumer의 재빌드 및 재배포가 필요하다)
두번째 방법은 개발이 정말 쉬워진다는 장점이 있지만 스키마의 실시간 변경에 대응이 어렵다는 치명적인 문제점을 가지고 있어 범용적으로 사용되고 있지는 않은 것 같다. 다만, 스키마의 실시간 변경이 잦지 않고, 변경이 일어나더라도 관련 시스템의 재배포가 수월한 상황이라면 Git Submodule을 이용한 방법도 나쁘지 않은 것 같다.
다음 글에서는, 두 번째 방법인 Schema Repository 공유를 통한 스키마 관리 방법을 실습해 보도록 하자!