Kafka 컨슈머 구축하기
사용 시기
- Kafka 컨슈머/프로듀서를 작성하거나 디버깅할 때
- 오프셋 커밋 전략과 전달 보장을 선택할 때
- 컨슈머 그룹 병렬 처리, 리밸런싱, 또는 데드레터 처리를 튜닝할 때
- 스트림 프로세싱/윈도잉에는 사용하지 마세요 (
processing-streaming-data사용)
워크플로
- [ ] 타겟 병렬 처리량에 맞게 파티션 크기 조정 (컨슈머 수 <= 파티션 수)
- [ ] 성공적인 처리 후에 오프셋 커밋
- [ ] 싱크를 멱등적으로 만들기 (이벤트 키 기준 upsert)
- [ ] 리밸런스 처리 (revoke 시 커밋, 긴 poll 간격 방지)
- [ ] 포이즌 메시지를 데드레터 토픽으로 라우팅
- 파티션이 병렬 처리량을 결정합니다. 컨슈머 그룹은 파티션 수까지만 확장 가능하며, 추가 컨슈머는 유휴 상태가 됩니다. 피크 처리량에 맞춰 파티션을 선택하세요.
- 처리 후에 커밋하세요. 작업이 안전하게 완료된 후에 오프셋을 커밋하고, 그 전에 커밋하지 마세요 — 일찍 커밋하면 크래시 시 메시지가 유실됩니다.
- 멱등적 싱크. 최소 한 번 전송은 재시도 시 중복을 의미합니다. 안정적인 이벤트 키로 upsert하여 재처리가 무해하도록 만드세요.
- 리밸런스는 발생합니다. 파티션 revoke 시 커밋하고
poll()간격을max.poll.interval.ms미만으로 유지하여 브로커가 컨슈머를 축출하지 못하게 하세요. - 포이즌 메시지는 오류와 함께 데드레터 토픽으로 전송하여, 하나의 잘못된 레코드가 파티션 전체를 막지 못하게 하세요.
패턴
처리 후 수동 커밋:
consumer = KafkaConsumer("orders", group_id="etl",
enable_auto_commit=False,
max_poll_records=500)
for msg in consumer:
try:
upsert(process(msg)) # 키 기준 멱등 처리
consumer.commit() # 성공 후에만 커밋
except PoisonError:
send_to_dlq(msg)
consumer.commit() # 잘못된 레코드는 건너뜀
멱등적 / 트랜잭셔널 프로듀서 — enable.idempotence=true를 설정하여 (재시도 중복 제거) 트랜잭션을 사용해 토픽 간 읽기-처리-쓰기 정확히 한 번을 구현합니다.
처리량 튜닝 — max.poll.records, fetch.min.bytes를 늘리고 배치로 처리하세요. 리밸런스 축출을 피하기 위해 처리를 빠르게 유지하세요.
일반적인 함정
- 자동 커밋 + 느린 처리 — 오프셋이 작업 완료 전에 진행되어 크래시 시 메시지가 유실됩니다. 처리 후 수동 커밋을 선호하세요.
- 파티션 수보다 많은 컨슈머 — 여분의 컨슈머는 유휴 상태가 됩니다. 확장을 위해 파티션을 재분배하세요.
- poll 사이의 긴 처리 시간 —
max.poll.interval.ms를 초과하여 무한 리밸런스를 유발합니다. 제한된 배치로 처리하거나 백그라운드 워커를 사용하세요. - 데드레터 경로 없음 — 하나의 포이즌 메시지가 전체 파티션을 차단합니다.
- 멱등적 싱크 없이 정확히 한 번 전송에 의존 — 최소 한 번 전송 단계가 있으면 중복이 다시 발생합니다.