테키테크 TEKITECH

[Kafka Study] 05. 프로듀서의 내부 동작 원리와 구현 w/실전 카프카 개발부터 운영까지 본문

Tech/Data Processing

[Kafka Study] 05. 프로듀서의 내부 동작 원리와 구현 w/실전 카프카 개발부터 운영까지

TEKI 2022. 4. 8. 23:36

5장과 6장에서는 프로듀서와 컨슈머의 내부 동작과 원리, 그리고 구현 방법을 다룬다. 한 번에 모두 소화하기는 쉽지 않아서 우선 프로듀서 내부 동작 원리를 이해하는 것만을 목표로 5장을 공부해보았다. 

 

목차


  • 프로듀서와 카프카
  • 배치 전송 방식과 개선 전략
    • 1. 지연시간 최소화: 라운드 로빈 전략과 스티키 파티셔닝 전략
    • 2. 압축 전송
    • 3. 중복 없는 전송
    • 4. 정확히 한 번 전송

프로듀서와 카프카

카프카는 성능 향상을 위해 병렬 처리를 지향한다. 따라서 메시지는 토픽 별로 관리하고, 토픽은 최소 하나 또는 둘 이상의 파티션으로 나누어 구성하며, 각 파티션의 데이터는 용량에 따라 로그 세그먼트로 나누어 저장한다. 반면 프로듀서는 빠르고 안정적인 성능을 위해 배치 처리, 압축 전송 등의 방식으로 트래픽을 최소화하는 것을 추구한다. 따라서 프로듀서는 카프카의 구조를 유지하며 배치 방식으로 전송하기 위해 시리얼라이저와 파티셔너를 추가한 디자인으로 완성되었다.

제가 쉽게 이해하기 위해 그린 그림으로, 사실과 다를 수 있습니다.

 

3장에서 레코드 구조와 send()메소드 및 재시도 동작 등 전송 방식에 대해서 간단하게 살펴보았다면, 이번 장에서는 실시간 메시징 시스템에서 배치 방식으로 전송하면서 발생하는 처리 지연, 중복 및 손실 등의 이슈에 대해 어떻게 대처하고 있는지 알아보았다.

 

 

배치 전송 방식과 개선 전략

프로듀서가 카프카에 레코드를 전송하는 비용을 줄이기 위해 배치 전송을 사용한다고 했다. 하지만 카프카 사용 목적에 따라 전송 효율 높이는 것보다 지연 시간을 최소화하여 실시간으로 메시지를 처리하는 것이 중요한 경우도 있다. 이 경우엔 프로듀서의 버퍼 메모리에서 메시지가 대기하는 최대 시간(linger.ms)을 0으로 설정하는 등 단 건의 메시지도 즉시 전송할 수 있도록 설정해야 한다. 목적에 따라 적절하게 설정하는 것이 중요하다는 점에 유의하면서 배치 전송 방식을 이해해보자.

 

1. 지연시간 최소화: 라운드 로빈 전략과 스티키 파티셔닝 전략

파티셔너는 카프카의 여러 파티션 중 어느 파티션으로 레코드를 보낼지 결정해준다. 기본적으로 프로듀서는 레코드의 키를 해시 처리해서 레코드를 보낼 파티션을 특정한다. 만약 키값을 사용해서 메시지를 전송하고 있다면, 파티션 수를 늘렸을 때 매핑 테이블이 변경되어 동일한 파티션으로 메시지가 가지 않을 수 있다는 점에 주의해야 한다. 그런데 이 키값은 필수 요소가 아니다. 보낼 주소도 없는데 메시지를 어떻게 보내나 싶은데 그게 된다.

3장에서 공부한 내용 다시보기 (https://teki.tistory.com/69)

 

프로듀서 레코드의 키값을 지정해주지 않으면 null로 설정된다. 이렇게 null 값을 갖는 레코드만 모아서 카프카에 전송한다(배치). 레코드를 모으는 방식은 2019년 출시된 아파치 카프카 2.4 버전을 기점으로 전(라운드 로빈)과 후(스티키 파티셔닝) 버전이 서로 다른 전략을 취한다.

[RoundRobinPartitioner]
라운드 로빈 전략을 취하는 경우 파티션마다 메시지를 순서대로 전송할 수 있다는 장점이 있다. 하지만 파티션마다 전송 가능한 레코드가 충분히 모일 때까지 대기해야 한다는 치명적인 단점이 있다. 이를 보완하기 위해 전송을 위한 최소 레코드 수 외에 최대 대기 시간을 설정해서 너무 오래 기다리지 않도록 설정할 수 있지만, 이 경우 압축 효율이 낮아진다는 또 다른 단점이 생긴다. 또한, 대상으로 하는 파티션이 많을수록 프로듀서는 더 많은 연산을 해야 한다는 문제도 고려해야 한다.

[UniforStickyPartitioner]
반면, 스티키 파티셔닝 전략에 따라 수정된 로직은 파티션의 수가 아무리 많아도 하나로 묶어서 전송할 수 있기 때문에 라운드 로빈 방식의 단점이 보완된다. 실제로 컨플루언트의 테스트 결과에 따르면 스티키 파티셔닝 전략을 적용한 결과 약 30% 이상 지연시간이 감소하고, 프로듀서의 CPU 사용률도 줄어들었다.

https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/

 

 

2. 압축 전송

(3장에서 건너뛰었던 내용) 앞에서 레코드 key와 value에 대해서 언급했는데, 이 데이터마다 SerDes(Serializer/Deserializer)를 데이터 타입으로 가진다. 여기에는 String, Short, Integer, Long, Double, Bytes 등 기본 텍스트 타입뿐만 아니라 XML, JSON, AVRO, Protobuf 등도 있다.

Serializer and Deserializer (https://www.baeldung.com/kafka-custom-serializer)

이렇게 다양한 형식과 크기의 메시지를 카프카에 저장하기 위한 공간을 계산하려면 네 가지 요소를 고려해야 한다.

  • 평균 메시지 크기 (Average message size)
  • 일일 메시지 전송량 (Messages per day)
  • 최대 저장 기간 (Retention period(days))
  • 리플리케이션 팩터 수

필요한 디스크 공간을 계산하는 식은 아래와 같다.

Average message size × Messages per day × Retention period × Replication Factor

예를 들어,

  • 평균 메시지 크기: 10kb
  • 일일 메시지 전송량: 1,000,000건
  • 최대 저장 기간: 5일
  • 리플리케이션 팩터: 3

라고 할 경우, 필요한 최소 디스크 공간은 10 × 1,000,000 × 5 × 3 = 150,000,000kb 즉 143GB로 산정할 수 있다. (당연히 실제 운영 시에는 재전송, 오류 등을 고려해 좀 더 여유를 두어야 할 것이다). 많은 데이터를 저장할수록 비용이 증가하는 만큼, 메시지 압축이 중요해진다. 또한 프로듀서의 배치 전송과 같은 케이스를 위해서도 카프카에서는 메시지 압축을 지원한다. 메시지 압축을 활용할 수 있는 상황에는..

  • 최소한의 메시지 전송 지연이 허용될 때
  • 디스크 및 네트워크 대역폭 절약을 위해 CPU 리소스를 더 사용할 수 있을 때
  • 데이터 빈도가 높아서 배치 전송이 원활할 때
  • 서버 로그나 XML, JSON 데이터와 같이 압축률이 높은 데이터를 전송할 때

등을 꼽을 수 있다.  카프카 및 클라이언트에서 지원하는 메시지 압축 포맷에는 gzip, snappy, lz4, zstd 등이 있다. 높은 압축률을 선호한다면 gzip이나 zstd를, 낮은 지연시간을 선호한다면 lz4나 snappy를 선택하는 등 특성을 잘 이해하고 필요에 따라 선택하면 된다.

https://developer.ibm.com/articles/benefits-compression-kafka-messaging/

압축 타입은 아래와 같이 compression.type 옵션으로 설정해주는데, 기본값은 '압축하지 않음'을 의미하는 none으로 저장되어 있다고 한다. 브로커에서 메시지 압축이 필요하다면 토픽 생성 시 아래와 같이 포맷 이름을 넣어주면 된다.

# (default) 압축하지 않음.
--config compression.type=none
# 압축 설정 (gzip)
--config compression.type=gzip

카프카 프로듀서(Java 기반)에서 메시지 압축 전송을 하려면 아래와 같이 설정할 수 있다.

kafkaProducerProps.put(“compression.type”, “<compression-type>”);
kafkaProducerProps.put(“linger.ms”, 5); //to make compression more effective

 

  

3. 중복 없는 전송

메시지 전송에는 '적어도 한 번 전송(at-least-once)', '최대 한 번 전송(at-most-once)', '정확히 한 번 전송(exactly-once)'등의 방식이 있다. 메시지 일부가 중복되더라도 메시지가 최소한 하나는 있음을 보장하는 방식이 '적어도 한 번 전송' 방식이라면, 메시지가 유실되는 것을 감수하고라도 한 건이라도 중복이 없도록 보장하는 방식이 '최대 한 번 전송' 방식이라고 볼 수 있다. 카프카의 '중복 없는 전송'은 기본적으로 '적어도 한 번 전송' 방식을 기반으로 동작한다.

https://medium.com/sfu-cspmp/in-depth-understanding-of-apache-kafka-in-10-minutes-255da627b9e

 

enable.idempotence 옵션이 false로 설정되어 있으면 중복 없는 전송이 보장되지 않고 있는 것이다. 중복 없는 메시지 전송이 필요하다면 이 옵션을 true로 바꿔주고, max.in.flight.requests.per.connectiion, acks, retries 등의 옵션을 추가로 설정해주어야 한다. 여기에서 idempotence, 즉 멱등성은 동일한 작업을 여러 번 수행하더라도 결과가 달라지지 않는 것을 의미한다.

 

 

4. 정확히 한 번 전송

중복 없는 메시지 전송을 위해 메시지를 비교하는 동작이 이루어지기 때문에 오버헤드가 당연히 존재한다. 하지만 컨플루언트 블로그에 따르면 카프카에서 해당 동작을 적용하면 기존 성능 대비 약 20% 정도만 감소했다고 한다. 따라서 일반적인 상황이라면 '중복 없는 전송'을 하도록 설정하는 것이 좋다. 하지만 반대로 메시지 중복이나 손실이 매우 치명적인 시스템도 있다. 이런 경우에는 '정확히 한 번 전송'이 보장되어야 한다. 정확히 한 번 전송이 성립하려면 중복 없는 전송일 필요가 있기도 하고, 전송에 실패한 메시지들은 한 번 이상 전송해야 하기도 한다. 즉, 중복 없는 전송은 정확히 한 번 전송의 일부 기능이라고 할 수 있다.

이 복잡한 전송 프로세스를 유지하기 위해 카프카는 트랜잭션 API라고 부르는 프로세스를 가진다. 트랜잭션이란 데이터베이스와 같은 시스템에서 이루어지는 논리적인 작업 단위를 말하며, ACID를 성립하는 것을 목표로 한다.

  • Atomicity: 원자성
  • Consistency: 일관성
  • Isolation: 독립성
  • Durability: 영속성

프로듀서가 카프카로 정확히 한 번 방식으로 메시지를 전송할 때, 메시지는 전체 실행 또는 전체 실패로 처리된다. (트랜잭션의 성질 중 A에 해당하는 원자성). 이를 위해 서버 측에서 트랜잭션 코디네이터Transaction Coordinator가 프로듀서로부터 전송된 메시지를 관리하며, 커밋 또는 중단 등을 표시한다. 그리고 저장된 메시지들이 정상 커밋된 것인지 실패한 것인지 식별하기 위해 컨트롤 메시지라는 타입의 메시지가 추가로 사용된다. 이 컨트롤 메시지는 오직 브로커와 클라이언트 통신에서만 사용되며, 페이로드에 애플리케이션 데이터 즉, 메시지의 밸류를 포함하지 않아 애플리케이션에 노출되지 않는다.

 

 


시스템을 기반으로 카프카를 운영하는 아이디어에 대해 생각해보는 흥미로운 시간이었다. 특히, 메시지 전송에서 중복과 유실의 관점에서 이해해보았다는 점이 많은 공부가 되었다. 아쉬운 점은 모르는 게 너무 많다 보니 한 장을 공부하는 데에도 시간이 오래 걸린다. 혼자 했으면 한없이 늘어졌을 텐데 스터디 방에서 꾸준히 공부하시는 분들이 보이니 나도 꾸준히 책을 펼치고 있다. 앞으로는 이론을 중심으로 공부하고, 실습은 좀 더 천천히 해봐야겠다.

※ Kafka Study 순서 ※

더보기

01. 카프카 특징과 이용 사례
02. 카프카 기본 개념과 구조
03. 카프카 실습 환경 구성 및 프로듀서와 컨슈머 기본동작과 예제
04. 카프카의 내부 동작 원리와 구현

05. 프로듀서의 내부 동작 원리와 구현
06. 컨슈머의 내부 동작 원리와 구현
07. 카프카 운영과 모니터링
08. 카프카 버전 업그레이드와 확장
09. 카프카 보안
10. 스키마 레지스트리
11. 카프카 커넥트
12. 엔터프라이즈 카프카 아키텍처 구성 사례
13. 카프카의 발전과 미래

 

※ 참고 자료와 이미지 출처 ※

더보기

[1] 실전 카프카 개발부터 운영까지, http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9791189909345#N
[2] Apache Kafka Producer Improvements with the Sticky Partitioner, https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/

[3] Message compression in Apache Kafka, https://developer.ibm.com/articles/benefits-compression-kafka-messaging/
[4] 카프카 프로듀서 파티셔너 종류 및 정리(2.5.0 기준), https://blog.voidmainvoid.net/360

[5] JSON Schema Serializer and Deserializer, https://docs.confluent.io/platform/current/schema-registry/serdes-develop/serdes-json.html
[6] Custom Serializers in Apache Kafka, https://www.baeldung.com/kafka-custom-serializer
[7] In-Depth Understanding of Apache Kafka in 10 Minutes, https://medium.com/sfu-cspmp/in-depth-understanding-of-apache-kafka-in-10-minutes-255da627b9e   

반응형
Comments