본문 바로가기

(노력이 많이 들어간)정리

카프카 파티션 정책에 관하여

kafka에 대한 자세한 설명은 생략하고, kafka와 파티션에 대한 이야기를 진행한다.

 

우선 kafka는 메시지 큐의 한 종류인 이벤트 브로커로서

기존 producer/consumer 구조에서는 각 애플리케이션들이 커넥션을 맺어야했지만,

kafka를 사용하면 kafka에서 연결들을 관리해줘서 괜찮다.

 

kafka에 있는 메시지 큐를 topic이라고 한다.

그리고 topic의 데이터가 너무 많아지는 경우가 있을 수 있기 때문에, partition이라는 개념이 있다.

 

partition은 물리적인 저장단위로, topic은 1개 이상의 partition으로 저장된다.

 

그러면 여기에 생각해 볼 것이 있다.

 

producer -> partition -> consumer로 구성되는데

 partition, consumer는 여러개일 수 있다.( 물론 producer도)

 

1) producer는 데이터를 어떤 patition에 넣을 것이며, 

2) consumer는 어떤 partition에서 데이터를 가져올까? 

 

1에 관한 것을 정해주는 객체를 partitioner ( 또는 producer partitioner)

2에 관한 것을 정해주는 객체를 partition assignor(또는 consumer partitioner) 라고 한다.

 

partitioner

 

partitioner의 종류에는 크게

1) 데이터키가 있는 경우

2) RoundRobinPartitioner

3) UniformStickyPartitioner

4) CustomPartitioner

 

 1)데이터키가 있는 경우는 데이터키의 값을 해시함수를 사용하여 해시값을 만들고, 그것을 파티션의 개수로 나눠서 나머지에 해당하는 파티션에 데이터를 전송한다. 

2) 라운드 로빈 방식은 최대한 균등하게 파티션에 데이터를 보내는 것이 목적이다.

3) 유니폼 스티키 파티셔너는 라운드 로빈방식이지만, 데이터를 묶어서 전송한다.(배치) 그렇기 때문에 성능에서 이점을 가진다.

 

4) 커스텀 파티셔너는 카프카가에서 제공하는 파티셔너가 아니라, 사용자가 직접 작성하는 파티셔너를 의미한다.

자바의 예시를 들자면 우선 partitioner interface를 implements 해야한다. 

 

1. 필수적으로 

 

partition, close, configure 함수를 구현해야한다

 

partition()

- topic: 토픽의 이름

- key: 파티션의 키( 키가 없으면 null)

- keyBytes: 직렬화된 key

- value: 밸류 오브젝트

- valueBytes: 직렬화된 value

- cluster: 현재 카프카 클러스터의 메타데이터 - 노드, 토픽, 파티션 등의 정보를 가지고 있음

리턴 값: 파티션번호 

 

close(): 파티셔너가 종료시 호출된다.

 

configure: 커스텀 프로퍼티를 얻는데 사용하는 함수, 프로퍼티나 설정을 사용하지 않으면 비워두자

 

생성한 파티셔너 등록 방법

 

그렇다면 유동적으로 파티셔너의 값을 바꾸는 방법은 없을까?

 

 -> Partitioner class를 Component로 등록하자!

 

partition assignor

partition assignor(consumer partioner)의 종류에는 크게 5가지가 있다.

 

  1. RangeAssignor
  2. RoundRobinAssignor
  3. StickyAssignor
  4. CooperativeStickyAssignor
  5. CustomAssignor

1. RangeAssignor

 

토픽의 파티션을 숫자기준으로 나열하고, 컨슈머의 이름을 사전순으로 나열한 뒤에 공평하게 배정한다. 정확히 나누어 떨어지지 않는 경우, 앞쪽 순서의 컨슈머가 더 많은 파티션을 처리한다.

2. RoundRobinAssignor

 

 컨슈머에서 번갈아가며 할당하는 방식. 컨슈머가 사용되는 횟수를 최대화 한다.

하지만 리밸런싱이 일어나면, 컨슈머의 변화를 최소화할 수 없다.

라운드 로빈 Asssignor의 문제 가능성: 컨슈머0이 토픽0을 구독하고, 컨슈머 1이 토픽 0,1을 구독, 컨슈머 2이 토픽 0,1,2를 구독하는 경우, 아래 그림과 같은 현상이 발생한다. 

 

3. StickyAssginor

 라운드로빈과 비슷하지만, 리밸런싱이 일어날 경우 최대한 파티션의 이동을 줄이는데 목적이 있음. 불필요한 파티션의 이동을 없애는 것은 컨슈머 성능에 긍정적인 영향.

1) 최대한 파티션들을 균등하게 분배한다. (더 높은 우선순위)

2) 파티션들이 이전에 할당된 컨슈머와의 매핑을 최대한 유지한다.

그렇게 하기 위한 규칙 "컨슈머에 매핑된 파티션의 개수 차이는 최대 1개로 한다. 또는 다른 컨슈머보다 파티션이 2개 이상 적은 컨슈머는 해당 토픽 파티션을 전송 받을 수 없다."

 

4. CooperativeStickyAssignor

균등 분배는 Sticky Assignor와 같지만, 리밸런싱 방식이 다르다.

Sticky Assignor => eager rebalancing protocol

Cooperative Sticky Assignor => Incremental cooperative reblancing

 

기존 방식들 -> 한 번에 파티션 분배를 처리하기 위하여, Stop the world로 모든 파티션을 정지하고 분배한다.

진행 순서

1. 가지고 있는 모든 파티션을 반환한다.

2. 반환된 파티션을 컨슈머 개수에 의해 적당히 분할

3. 실제로 각 컨슈머들에 파티션을 할당한다.

 

Incremental Cooperatvie Rebalancing -> 한 번에 파티션의 분배를 처리하지 않는다.

적은 숫자의 연속적인 리밸런싱 작업으로 최종적으로 균형적인 파티션 분배를 한다.

 

컨슈머가 추가되는 상황

1. 새로운 컨슈머에게 할당해 줄 파티션을 자발적으로 해제한다.

2. 해제된 파티션을 새로운 컨슈머에게 할당한다.

-> STW가 발생하지 않음

 

2.3 버전 이후의 브로커와 클라이언트 지원 

 

기존 방식은 리밸런싱이 발생할때 stop the world가 발생하여 모든 파티션이 정지되어서 처리량이 떨어진다. 

대량 데이터 처리에 있어서 치명적이다.

5. Custom Assignor

 

 custom partitioner와 마찬가지로 ConsumerPartitionAssignor class를 Implements하여 설정할 수 있다.

 

또는 ConsumerPartitionAssignor를 구현한 추상클래스 AbstractPartitionAssignor, AbstractStickyAssignor를 상속받아 구현한다.

단순화된 assign을 구현할 수 있다. 

Map<String, List<TopicPartition>> 

-> Topic에 넣을 (partition의 리스트)를 지정한다.

 

예시)  CPU의 리스트(0,1,2) , MEM의 리스트(3,4,5)

맵을 만들고, "CPU" 에 List<TopciPartition> , topicPartition("CPU",0), topicPartition("CPU",1), topicPartition("CPU",2)가 들어가도록 한 것을 리턴..(MEM도 마찬가지)

 

name(): unique한 이름을 사용한다.

 

 

Partioner와 마찬가지로 KafkaConsumer의 COnsumerFactory의 config에서 "ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG"의 키로 넣는다.

 

Component로 등록하면 중간에 다른 파티션으로 지정할 수 있다.

 

요약

  • 파티션은 물리적인 저장단위로, 토픽의 데이터가 많을 때, 저장공간의 부족을 극복하기 위해 사용한다. 
  • 프로듀서에서 데이터를 어떤 파티션에 전송할지를 partitioner가 결정하고, 컨슈머가 어떤 파티션의 데이터를 처리할지를 partition assignor가 결정한다.
  • partitioner의 종류에는 데이터의 키가 있는 경우, RoundRobinPartitioner, UniformStickyPartitioner, CustomPartitioner 등이 있다.
  • partition assignor의 종류에는 RangeAssignor, RoundRobinAssignor, StickyAssignor, CooperativeStickyAssignor, CustomAssignor 등이 있다.
  • 파티션을 추가하거나, 컨슈머를 추가/삭제 할때마다 리밸런싱이 발생한다. 

 

출처 및 참고자료

  1. https://www.youtube.com/watch?v=0Ssx7jJJADI
  2. https://www.youtube.com/watch?v=H_DaPyUOeTo
  3. https://www.youtube.com/watch?v=-vKiNUH5OT
  4. https://www.javatpoint.com/apache-kafka-advantages-and-disadvantages
  5. https://medium.com/@tas.com/
  6. https://free-strings.blogspot.com/2016/04/zero-copy.html
  7. https://blog.voidmainvoid.net/360
  8. https://blog.voidmainvoid.net/361
  9. https://www.confluent.io/blog/apache-kafka-producer-improvements-sticky-partitioner/
  10. https://medium.com/streamthoughts/understanding-kafka-partition-assignment-strategies-and-how-to-write-your-own-custom-assignor-ebeda1fc06f3
  11. https://kafka.apache.org/25/javadoc/org/apache/kafka/clients/consumer/StickyAssignor.html
  12. https://devidea.tistory.com/100
  13. https://kafka.apache.org/24/javadoc/org/apache/kafka/clients/consumer/ConsumerPartitionAssignor.html
  14. https://kafka.apache.org/24/javadoc/?org/apache/kafka/clients/producer/Partitioner.html