Kafka Producer Message Batching과 몇 가지 성능 튜닝 옵션들

kindof

·

2023. 7. 1. 19:54

이번 시간에는 카프카 프로듀서의 기본적인 구조와 레코드의 배치(Batch) 전송에 대해 소개합니다.

 

그리고 레코드의 배치 전송을 통해 얻을 수 있는 이득과 고려해야 할 점, 그리고 이와 관련된 성능 튜닝을 할 수 있는 몇 가지 옵션에 대해 이야기해보겠습니다.

 

 

1. Kafka Producer, Record Accumulator

먼저 카프카 프로듀서가 메시지를 전송하는 방식에 대해 간략히 짚어보겠습니다. 기본적으로 카프카 프로듀서는 아래 그림과 같은 구조를 바탕으로 메시지를 전송합니다.

Kafka producer

크게 Producer → Serializer → Partitioner → RecordAccumulator → Sender Thread → Broker 의 흐름을 따르는데요.

 

조금 더 구체적인 이해를 위해 메시지 전송 순서에 따라 org.apache.kafka.clients.producer의 구현 코드를 같이 살펴보겠습니다.

 

[1] Producer는 Topic, Key, Value, Partition 등의 정보를 가진 ProducerRecord 라는 메시지 객체를 send() 합니다. 

 

[2] 이 메시지는 Serializer에 의해 바이트 배열로 변환됩니다.

keySerializer.serialize()

 

[3] 이후, Key값을 바탕으로 Partitioner에 의해 어떤 토픽의 몇 번째 파티션으로 전송될 지 결정되고, RecordAccumulator의 배치에 추가(append) 됩니다.

paritition(), append()

 

[4] 목적지가 정해진 메시지는 RecordAccumulator의 배치에 추가되고, Sender Thread에 의해 브로커에게 전송됩니다.

sender.wakeup()

[5] 메시지가 성공적으로 전송되면 브로커는 Producer에게 Metadata를 리턴합니다.

 

지금까지 설명한 내용이 모두 KafkaProducer.java의 doSend(ProducerRecord<K,V> record, Callback callback) 메서드에 포함되어 있으니, 궁금하신 분들은 소스 코드를 한 번 봐보시는 것을 추천드립니다(별로 길지 않습니다).

 

 

2. Kafka Producer Message Batching

한편, 위에서 자연스럽게 설명하지 않고 넘어간 넘어간 내용 중 하나가 카프카 프로듀서의 메시지 배치 전송입니다.

 

카프카 프로듀서가 메시지를 배치 단위로 전송하는 목적은 결국 프로듀서의 처리량(Throughput)을 증가시키는 데 있는데요.

 

최종적으로 브로커에게 전달해야하는 메시지들을 적은 횟수의 네트워크 요청으로 보내면 그만큼 성능 향상이 향상 되는 것을 기대할 수 있기 때문입니다. 하지만, 그렇다고 해서 너무 많은 양의 메시지를 한 번에 보낸다면 메시지들의 처리 응답을 기다리는 Latency가 너무 길어지는 문제가 생길 수 있습니다.

 

결국, 카프카 프로듀서의 배치 메시지 전송은 애플리케이션에서 사용하고 있는 레코드의 특성과 여러 가지 환경을 고려해서 최적화해야 한다는 숙제가 있는 것입니다.

 

그렇다면 카프카 프로듀서의 메시지 배치 전송과 관련된 성능 튜닝에 대해 이야기해보겠습니다.

 

3. 성능 튜닝 포인트

3-1. batch.size 

batch.size는 단일 배치의 사이즈를 의미합니다. default 값으로 16384Byte이 설정되어 있는데, 이 값만큼 메시지들이 배치에 쌓이게 되면 모든 메시지가 한꺼번에 전송됩니다.

 

다만, batch.size 설정이 항상 해당 사이즈만큼의 메시지가 쌓일 때까지 전송을 미룬다는 뜻은 아닌데요. 프로듀서는 배치에 쌓인 메시지의 양과 관계없이 전송 가능한 상태가 되면 메시지를 전송하게 됩니다.

 

따라서 해당 값을 크게 설정한다고 해서 잦은 빈도로 메시지가 전송되는 것은 아니지만, 메시지의 크기에 비해 너무 작은 값을 설정해두면 잦은 메시지 전송으로 인한 오버헤드가 생길 수 있습니다.

 

3-2. linger.ms

linger.ms는 현재 배치를 전송하기 전까지 대기하는 시간을 결정합니다. 프로듀서는 현재 배치가 가득차거나 linger.ms에 설정된 제한 시간이 되었을 때 메시지 배치를 전송하는데요. 

 

default 0으로 설정된 linger.ms 값을 조정하면 Sender Thread가 하나의 배치를 가져갈 때 일정 시간을 대기하도록 함으로써 배치에 메시지를 충분히 쌓을 수 있는 시간을 마련할 수 있게 됩니다.

 

예를 들어, 메시지가 생성되는 속도가 linger.ms 값보다 큰 상황이라면 메시지가 한 번에 1개씩만 브로커로 전달되기 때문에 배치 처리의 효율성이 매우 떨어질 수 있습니다. 따라서, 이 경우에는 Producer Requests에 포함된 레코드 수를 보면서 linger.ms 값을 조금씩 늘려보는 것이 도움될 수 있습니다.

 

3-3. compression.type

메시지 배치 전송에서 기본적으로 메시지들은 압축되지 않은 상태로 전송됩니다. 하지만 compression.type을 snappy, gzip, zstd 등의 알고리즘으로 활성화하면 메시지를 압축한 뒤 브로커로 전송하게 됩니다.

 

각 압축 알고리즘의 장단점은 다르지만, 압축 기능을 활성화함으로써 카프카로 메시지를 전송할 때 자주 발생하는 네트워크 병목과 저장 공간 이슈를 해결할 수 있습니다.

 

 

4. 정리

이번 글에서는 카프카 프로듀서의 메시지 배치 전송, 그리고 이와 관련된 몇 가지 성능 튜닝 포인트에 대해 정리했습니다.

 

서비스 환경에 따라 메시지의 생성 속도, 각 메시지의 크기, 컨슈머들의 성능 등이 다르기 때문에 위에서 설명한 옵션값들은 다를 수밖에 없습니다.

 

만약 카프카 성능 개선이 필요하다면, 기존에 운영하고 있는 카프카 환경의 지표들을 살펴보고, 개선이 필요한 파라미터들을 수정하는 방식이 도움될 것 같습니다.

 

5. Reference

https://medium.com/lydtech-consulting/kafka-producer-message-batching-2f6f0b75a19c

카프카 핵심 가이드 대규모 실시간 데이터와 스트림 처리 그웬 샤피라, 토드 팔리노, 라지니 시바람, 크리트 페티 저/이동진 역