Kafka - Consumer 2

develkkm·2025년 11월 10일

Consumer Group & Rebalancing


GroupCoordinator

GroupCoordinator는 Consumer Group을 관리하고 그룹 내 Consumer들의 참여·이탈·세션 유지·리밸런싱 등을 담당하는 Broker 단위의 컴포넌트이다.

Consumer가 그룹에 참여하면 JoinGroup 요청을 Coordinator에게 보내고 Coordinator는 그룹 내 멤버 목록과 파티션 할당 정보를 관리한다.
이후 SyncGroup 요청을 통해 각 Consumer에게 자신이 담당할 파티션 정보를 전달한다.
Consumer는 이 정보를 바탕으로 Fetch 요청을 수행한다.

GroupCoordinator의 동작 과정

  1. JoinGroup 단계
    새로운 Consumer가 Group에 참여하면 Coordinator에게 JoinGroup 요청을 보낸다.
    첫 번째로 들어온 Consumer가 Leader 역할을 맡는다.

  2. SyncGroup 단계
    Leader는 그룹 내 모든 Consumer의 구독 정보를 취합해 파티션 할당 전략(range, round-robin 등)을 적용하고, 그 결과를 Coordinator에 전달한다.
    Coordinator는 이를 다른 Consumer들에게 전달하여 할당 정보를 동기화한다.

  3. Heartbeat 유지 단계
    각 Consumer는 주기적으로 Heartbeat를 Coordinator에게 보내 세션이 살아있음을 알린다.
    일정 시간 동안 Heartbeat가 수신되지 않으면 Consumer는 그룹에서 제거되고, Rebalancing이 발생한다.


Rebalancing

Rebalancing은 Consumer Group 내의 멤버 변화(추가, 제거, 세션 만료 등)와 Partition 수의 변화로 인해 파티션 재분배가 필요할 때 발생한다.

이 과정에서 모든 Consumer는 데이터를 일시적으로 소비하지 못하며 Coordinator가 새롭게 파티션을 분배한 후 정상적으로 재시작된다.

리밸런싱은 크게 세 가지 이벤트로 발생한다.

  • 새로운 Consumer가 그룹에 참여할 때
  • 기존 Consumer가 종료 또는 실패할 때
  • 일정 시간 동안 poll() 또는 heartbeat를 수행하지 않아 세션이 만료될 때
  • 해당 Topic에 새로운 Partition이 추가되는 경우

Consumer Group 상태 전이

상태설명
Empty그룹에 Consumer가 없는 상태
PreparingRebalance리밸런싱이 준비 중인 상태 (JoinGroup 요청 수집 중)
CompletingRebalance파티션 할당이 완료되어 SyncGroup으로 전달 중
Stable모든 Consumer가 할당을 완료하고 Fetch를 수행 중
DeadCoordinator가 그룹 정보를 삭제한 상태

Consumer Static Group

일반적으로 Consumer는 재시작 시 새로운 멤버 ID로 인식되어 불필요한 리밸런싱이 발생한다.
이를 방지하기 위해 Static Group 기능을 사용한다.

group.instance.id를 지정하면 session.timeout.ms시간 동안 Consumer가 재시작해도 동일한 ID로 인식되어 기존 파티션을 유지한다.
따라서 일시적인 장애나 재시작에도 리밸런싱이 발생하지 않아 안정적인 처리 흐름을 보장한다.

Static Group 동작 과정

  1. Consumer가 group.instance.id를 포함한 JoinGroup 요청을 보낸다.
  2. GroupCoordinator는 해당 ID를 기준으로 기존 Consumer와 동일한 멤버로 인식한다.
  3. 기존 세션session.timeout.ms이 만료되지 않은 한, 파티션 재분배Rebalancing는 발생하지 않는다.

Static Group 관련 설정값

설정값설명
group.instance.idConsumer를 고정 멤버로 식별하기 위한 ID. 이 값을 설정하면 Static Group이 활성화된다.
session.timeout.msCoordinator가 Consumer로부터 Heartbeat를 받지 못하면 해당 Consumer를 제거하는 시간.

Heartbeat Thread와 poll()

Heartbeat Thread는 Consumer가 GroupCoordinator와의 연결을 유지하기 위한 백그라운드 스레드이다.

이 스레드는 일정 주기로 Coordinator에 Heartbeat를 보내며, Consumer가 정상적으로 동작하고 있음을 알린다.

poll() 메서드는 너무 오래(block)되거나 호출 주기가 길어지면 세션이 만료될 수 있다.
그 결과 Coordinator는 해당 Consumer를 그룹에서 제거하 Rebalancing이 발생한다.
따라서 poll()은 주기적으로 호출되어야 하며, 내부적으로 Heartbeat도 정상 수행되도록 해야 한다.

Heartbeat & Poll() 설정값

설정값설명
heartbeat.interval.msHeartbeat 전송 주기 (보통 session.timeout의 1/3 이하로 설정)
max.poll.interval.msConsumer가 poll()을 호출하지 않고 머무를 수 있는 최대 시간. 초과 시 세션 만료 및 리밸런싱 발생

Partition Assignment Strategy

Rebalancing 시 파티션이 Consumer에게 어떻게 분배될지를 결정하는 전략이다.

Kafka는 크게 EagerCooperative 두 가지 방식으로 나뉜다.

Eager 방식

Eager는 모든 Consumer가 즉시 파티션을 반납하고, Coordinator가 전체를 다시 분배하는 방식이다.
따라서 리밸런싱이 발생하면 일시적으로 모든 Consumer가 메시지를 읽지 못한다.

  • Range

    • 각 토픽 파티션을 순서대로 Consumer에게 나누어 할당
    • 파티션 수가 Consumer 수로 나누어떨어지지 않으면 앞쪽 Consumer가 더 많은 파티션을 담당
    • 예: 6개 파티션, 2개 Consumer → C1: [0,1,2], C2: [3,4,5]
  • RoundRobin

    • 파티션을 순서대로 Consumer에 번갈아가며 배정
    • Consumer 간의 파티션 수 균형이 잘 맞음
    • 예: C1: [0,2,4], C2: [1,3,5]
  • Sticky

    • 가능한 한 기존 할당을 유지하면서 새 Consumer에게만 최소한의 변경으로 분배
    • Consumer 추가/제거 시 파티션 이동이 최소화됨

Cooperative 방식

Cooperative Sticky는 기존 Eager의 전체 중단 문제를 해결하기 위해 일부 Consumer만 점진적으로 파티션을 반납하고 재할당한다.
이 덕분에 리밸런싱 중에도 나머지 Consumer는 계속 데이터를 처리할 수 있다.

이 방식은 Downtime 최소화Throughput 유지에 유리하여 실서비스 환경에서 널리 사용된다.


실습

https://github.com/develkkm/kafka-from-0/tree/kafka-core/consumers

profile
알던것을 더 확실하게

0개의 댓글