Kafka

StickyPartitioner를 사용할 때 메시지가 골고루 배분되지 못하는 현상

재심 2023. 1. 4. 23:35

[Overview]

Apache Kafka Client 2.4 ~ 3.2 버전에서는 크게 3가지의 파티셔너가 제공된다.

 

- RoundRobin (Key 해싱 X)

- Sticky (Key 해싱 X)

- Default (Sticky + Key 기반 해싱 보장)

 

StickyPartitioner: https://jaemni.tistory.com/entry/Sticky-Partitioner

이 때 Sticky 파티셔너를 사용했을 때 특정 파티션에 메시지가 몰리는 현상이 있다고 한다.

이에 대해 확인해보고, 직접 재현해본 내용을 다룸.

(이 문제로 인해 Apache Kafka 3.3 버전에서는 기존 파티셔너들이 Deprecated되고, BuiltInPartitioner가 신규로 등장하였다)

 

[이슈]

문제 제기: https://issues.apache.org/jira/browse/KAFKA-10888?focusedCommentId=17285383&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-17285383

Apache Kafka 3.3에서 BuiltInpartitioner가 나온 이유: https://cwiki.apache.org/confluence/display/KAFKA/KIP-794%3A+Strictly+Uniform+Sticky+Partitioner

 

이슈 리포팅 사례

  • UniformStickyPartitioner 사용시 0에 가까운 linger.ms 옵션 사용시 파티션별 메시지 size 불균형 관찰

문제점

UniformStickyPartitioner의 의도와는 다르게 파티셔닝이 균일하게 되지 않은 것으로 나타남

원인

StickyPartitioner의 경우 최대한 기존 배치에 메시지를 쌓아 전송하는 방식을 택하였고,

OnNewBatch 메서드가 호출되어야만 다음 파티션의 배치에 메시지를 쌓게된다. 그리고 OnNewBatch 메서드가 호출되는 경우는 크게 2가지이다.

 

  • 배치가 방금 전송된 경우
  • 배치가 꽉 찬 경우

Producer의 경우 흔히 linger.ms=0, batch.size=16384KB 인 기본값을 많이 사용하게 된다.

이 때 linger.ms가 0이더라도 브로커가 배치를 당장 받을 수 없는 상황 (ex: 네트워크 속도 저하) 이라면 메시지가 즉시 전송되지 않고, 메시지가 배치에 더 쌓일 수 있다.

(StickyPartitioner 내용 참조)

 

이 때 문제가 발생할 여지가 생긴다.

파티션이 3개가 있다고 가정하자. 이 때 파티션 배치는 아래와 같고, linger.ms=0, batch.size=16384를 사용한다고 가정한다.

 

파티션0 브로커0 (느린 브로커)
파티션1 브로커1
파티션2 브로커2

시나리오

  1. 메시지 "A"가 파티션0의 배치에 추가되었다.
  2. linger.ms=0이라서 즉시 전송하려고 하였으나 브로커0의 네트워크 상황이 좋지않아 바로 보내지 못하고 좀 더 대기하게 되었다.
  3. 메시지 "B"가 유입되었다.
  4. OnNewBatch가 호출된 상황이 아니므로 파티션0의 배치에 추가되었다.
  5. 메시지 "C"가 유입되었다.
  6. 마찬가지로 OnNewBatch가 호출된 상황이 아니므로 파티션0의 배치에 추가되었다.
  7. 1초가 지난 후 브로커0이 해당 배치를 받아주었다.
  8. 파티션0에는 3개의 메시지가 전송되었다.
  9. 메시지 "D"가 유입되었다.
  10. 이번에는 OnNewBatch가 호출된 상황 (배치가 방금 전송된 경우) 이므로 파티션1의 배치에 메시지 "D"가 추가되었다.
  11. linger.ms=0이므로 즉시 전송시도를 하였고, 브로커1은 상태가 좋아서 바로 받아주었다.
  12. 파티션1에는 1개의 메시지가 유입되었다.
  13. 메시지 "E"가 유입되었다.
  14. 마찬가지로 OnNewBatch가 호출되었고, 파티션2의 배치에 쌓이며, 브로커2의 상태도 좋아서 바로 전송되었다.
  15. 파티션2에도 1개의 메시지가 유입되었다.
  16. 메시지 "F"가 유입되었다.
  17. OnNewBatch가 호출되었고, 이번에는 파티션0의 배치에 쌓였다.
  18. linger.ms=0 이지만 브로커0이 여전히 느려서 바로 전송하지 못하였다.
  19. 메시지 "G"가 유입되었다.
  20. OnNewBatch 호출상황이 아니므로 파티션0의 배치에 쌓였다.
  21. 브로커0이 배치를 수신하였다.

=> 결과적으로 가장 느린 브로커0 에는 메시지가 5개있고, 빠른 브로커인 1,2는 메시지가 각각 1개씩 밖에 없는 상황이 연출되었다.

 

이 상황이 반복되면서 메시지 정체현상이 가속화되는 문제가 Sticky Partitioner에서 발생하는 것

결국 아래와 같은 결과가 발생할 수 있다.

파티션 TotalBatches TotalBytes TotalRecords BytesPerBatch RecordsPerBatch
0    1683          25953200   25228         15420.80     14.99        
1    1713          7836878    4622          4574.94      2.70
2    1711          7546212    4381          4410.41      2.56

 

[재현해보기]

준비물

  • 브로커 5대
  • 토픽 1개 - 파티션 수 15 (브로커 수의 배수), Replication Factor 1
  • ping -s 옵션으로 네트워크 부하 주기
  • Apache Kafka-Client Version 2.8 (2.4~3.2 버전이면 됨)

 

토픽생성

sudo kafka-topics \
    --create \
    --bootstrap-server localhost:9092 \
    --topic jaeshim_test \
    --partitions 15 \
    --replication-factor 1 \
    --config min.insync.replicas=1

토픽 Describe

여기서 1번 브로커에만 ping -s 옵션으로 부하를 주어 네트워크 사용량을 늘릴 예정이다.

즉, 파티션 3,8,13에 메시지가 훨씬 많이 쌓이면 재현 성공임.

sudo kafka-topics \
    --describe \
    --bootstrap-server localhost:9092 \
    --topic jaeshim_test
    
    
-----------------------------------------

Topic: jaeshim_test TopicId: h15ZvLlWRuejiE0b-lz8yQ PartitionCount: 15      ReplicationFactor: 1    Configs: min.insync.replicas=1,segment.bytes=524288000,retention.bytes=3221225472
        Topic: jaeshim_test Partition: 0    Leader: 2       Replicas: 2     Isr: 2  Offline:
        Topic: jaeshim_test Partition: 1    Leader: 5       Replicas: 5     Isr: 5  Offline:
        Topic: jaeshim_test Partition: 2    Leader: 3       Replicas: 3     Isr: 3  Offline:
        Topic: jaeshim_test Partition: 3    Leader: 1       Replicas: 1     Isr: 1  Offline:
        Topic: jaeshim_test Partition: 4    Leader: 4       Replicas: 4     Isr: 4  Offline:
        Topic: jaeshim_test Partition: 5    Leader: 2       Replicas: 2     Isr: 2  Offline:
        Topic: jaeshim_test Partition: 6    Leader: 5       Replicas: 5     Isr: 5  Offline:
        Topic: jaeshim_test Partition: 7    Leader: 3       Replicas: 3     Isr: 3  Offline:
        Topic: jaeshim_test Partition: 8    Leader: 1       Replicas: 1     Isr: 1  Offline:
        Topic: jaeshim_test Partition: 9    Leader: 4       Replicas: 4     Isr: 4  Offline:
        Topic: jaeshim_test Partition: 10   Leader: 2       Replicas: 2     Isr: 2  Offline:
        Topic: jaeshim_test Partition: 11   Leader: 5       Replicas: 5     Isr: 5  Offline:
        Topic: jaeshim_test Partition: 12   Leader: 3       Replicas: 3     Isr: 3  Offline:
        Topic: jaeshim_test Partition: 13   Leader: 1       Replicas: 1     Isr: 1  Offline:
        Topic: jaeshim_test Partition: 14   Leader: 4       Replicas: 4     Isr: 4  Offline:

Kafka-Perf-Test 구동

kafka-producer-perf-test ^
  --topic jaeshim_test ^
  --num-records 9000000 ^
  --record-size 1 ^
  --throughput 5000 ^
  --print-metrics ^
  --producer-props acks=1 ^
  bootstrap.servers={server}:9092

Ping -s 옵션으로 브로커1에 네트워크 사용량 늘리기

sudo ping -s 4 {ip} -f -l 33000

결과

$ kafka-run-class kafka.tools.GetOffsetShell   --broker-list {server}:9092 --topic jaeshim_test
--------------------------------
//3,8,13 파티션에 약 9배 정도 더 유입되었음
jaeshim_test:0:311557
jaeshim_test:1:312023
jaeshim_test:2:310199
jaeshim_test:3:2711788
jaeshim_test:4:309834
jaeshim_test:5:314388
jaeshim_test:6:315577
jaeshim_test:7:314106
jaeshim_test:8:2472378
jaeshim_test:9:313949
jaeshim_test:10:313933
jaeshim_test:11:319251
jaeshim_test:12:311068
jaeshim_test:13:2613606
jaeshim_test:14:310495

 

[해결방법]

근본적인 해결방법은 아니고, 임시 방편정도인듯하다.

가장 좋은 것은 역시 브로커의 상태를 건강하게 관리하는 것인듯하다.

 

  • 브로커의 상태를 잘..관리한다.. (막연함)
  • RoundRobin Partitioner를 사용한다. 하지만 Connection이 훨씬 많고, 배치를 효율적으로 사용하지 못하기 때문에 성능적으로 불리하다.
  • Apache Kafka 3.3의 BuiltInPartitioner를 활용한다.
  • 배치 사이즈를 16384에서 축소시켜 OnNewBatch가 좀 더 빠르게 호출되도록한다. 하지만 결국은 다른 파티션들을 한바퀴 순회하고나서 다시 느려진 파티션에서 정체되기 때문에 누적되면 동일한 결과일 것으로 예상됨.

'Kafka' 카테고리의 다른 글

Performance Management  (0) 2023.04.16
Kafka Fundamental Hands On  (0) 2023.02.04
Kafka Topic Naming Convention에 대해..  (0) 2022.12.13
Kafka Performance Tuning  (0) 2022.11.02
Zookeeper 구성에 관한 팁  (0) 2022.11.02