티스토리 뷰
728x90
요약
Kafka 컨슈머의 오프셋(commit) 전략은 “메시지를 정확히 한 번 처리할 수 있느냐, 아니면 중복·유실을 감수하느냐”를 결정합니다.
편리한 자동 커밋(enable.auto.commit=true)은 주기적 타이머로 오프셋을 저장하지만, 장애 시 이미 받은 레코드가 아직 커밋되지 않은 상태로 재처리·유실 위험이 큽니다.
따라서 실전 서비스에서는 수동 커밋 API(commitSync, commitAsync)로 “업무 로직 완료 ↔ 오프셋 저장” 을 직접 묶어야 합니다.
commitSync() 는 브로커 ACK를 받을 때까지 블로킹 → 안정성 최우선, 반면 commitAsync() 는 논블로킹·콜백 기반으로 처리량을 높이되 실패 재시도는 개발자가 책임져야 합니다.
많은 팀이 “루프 내 commitAsync + 종료 직전 commitSync” 패턴으로 속도와 안정성을 절충합니다.
아래에서는 자동 커밋의 한계, 수동 커밋 두 방식의 차이, 예외 처리·트랜잭션 연계 팁, Java/Python 예제를 단계별로 정리합니다.
1. 자동 커밋의 편리함과 함정
1.1 자동 커밋이란?
- 클라이언트는 auto.commit.interval.ms(기본 5 초)마다 현재 오프셋을 __consumer_offsets 토픽에 백그라운드로 기록합니다.
- 설정 한 줄로 끝나 개발 초기·단순 로그 수집에 자주 쓰입니다.
1.2 문제점
위험 설명
| 중복 처리 | 컨슈머가 배치 로직을 끝내기 전 장애 → 마지막 커밋 이후 레코드를 다시 읽음. |
| 데이터 유실 | at-most-once 패턴(수신 즉시 커밋)으로 바꾸면 장애 시 처리 전 레코드가 영영 손실. |
| 제3시스템 일관성 | DB 트랜잭션과 오프셋 저장 시점을 분리할 길이 없어 이중 커밋 문제 발생. |
2. 수동 커밋이 필요한 이유
2.1 commitSync() — 안정성 최우선
- 메서드가 블로킹되어 브로커 ACK를 확실히 받은 뒤 반환 → 커밋 실패 시 예외를 직접 처리.
- 자동으로 재시도(네트워크 오류 등)하므로 개발자가 추가 루프를 돌릴 필요가 없습니다.
- 단점은 RTT 만큼 지연이 늘어 TPS가 10~30 % 줄 수 있다는 보고.
2.2 commitAsync() — 처리량·지연 최적화
- 요청을 비동기 전송 후 바로 다음 폴링으로 넘어가 처리 속도를 극대화합니다.
- 브로커 실패 ↔ 네트워크 장애 시 재시도하지 않으므로 콜백에서 오류를 받아 직접 대응해야 안정성이 확보됩니다.
2.3 하이브리드 전략
- “루프마다 commitAsync() → 종료·리밸런스 직전 commitSync()” 패턴이 Confluent 가이드의 추천안입니다.
- 이렇게 하면 평상시엔 낮은 지연과 높은 처리량, 종료 경계에서는 정확성을 동시에 충족합니다.
3. 예제 코드
3.1 Java (수동 커밋 샘플)
Properties p = new Properties();
p.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
p.put(ConsumerConfig.GROUP_ID_CONFIG, "safe-demo");
p.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false"); // 자동 커밋 OFF
p.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
p.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
"org.apache.kafka.common.serialization.StringDeserializer");
try (KafkaConsumer<String,String> c = new KafkaConsumer<>(p)) {
c.subscribe(List.of("orders"));
while (running) {
ConsumerRecords<String,String> recs = c.poll(Duration.ofMillis(500));
for (ConsumerRecord<String,String> r : recs) {
process(r); // 업무 로직
}
c.commitAsync((offsets, ex) -> { // 빠른 루프
if (ex != null) log.error("async commit fail", ex);
});
}
// 정상 종료 시 마지막 배치 보증
c.commitSync();
}
- 예제는 async + final sync 패턴을 구현하여 성능과 안전성을 모두 확보합니다.
3.2 Python (kafka-python)
from kafka import KafkaConsumer, TopicPartition, OffsetAndMetadata
consumer = KafkaConsumer(
'orders',
bootstrap_servers='localhost:9092',
group_id='safe-demo',
enable_auto_commit=False,
auto_offset_reset='earliest'
)
try:
for msg in consumer:
process(msg.value)
# 파티션별 오프셋 객체 생성
tp = TopicPartition(msg.topic, msg.partition)
offsets = {tp: OffsetAndMetadata(msg.offset+1, None)}
consumer.commit_async(offsets=offsets)
except Exception as e:
print("error", e)
finally:
consumer.commit() # 종료 보증
consumer.close()
- commit_async() 는 프로미스가 없어 예외 로깅만 가능, 재시도는 직접 구현해야 함을 강조합니다.
4. 실전 전략 & 모범 사례
4.1 트랜잭션/외부 DB와 일관성 맞추기
- “DB 커밋 ➜ Kafka 오프셋 커밋” 순서로 2-Phase 느낌의 애플리케이션 가상 트랜잭션을 구성하라.
- 확실한 Exactly-Once 가 필요하면 Kafka 트랜잭션 API + 멱등 프로듀서 로 전환.
4.2 예외·리밸런스 대비 콜백 처리
- commitAsync(callback) 에서 실패 오프셋을 로그 + 재시도 큐 로 남겨 다음 폴링에서 commitSync() 로 복구한다.
- 리밸런스 리스너(ConsumerRebalanceListener)에서 파티션 회수 전 commitSync() 호출해 중복 소비 가능성을 최소화.
4.3 모니터링 지표
메트릭 역할
| records-lag | 배치 지연이 커지면 커밋 주기·처리 속도 점검 |
| commit-latency-avg/max | 브로커 ACK 지연 관찰 → 네트워크·I/O 병목 파악 |
| commit-failure-rate | Async 오류 빈도 모니터링, 알람 설계 |
5. 결론
- 자동 커밋은 개발 초기·손실 허용 시나리오에만 쓰고,
비즈니스 데이터에는 반드시 enable.auto.commit=false + 수동 커밋을 적용하세요. - commitAsync() + (종료 or 리밸런스) commitSync() 패턴이 가장 널리 검증된 절충안입니다.
- 외부 DB·캐시와 “같이 커밋”해야 한다면 오프셋 저장 타이밍을 업무 트랜잭션과 동일 영역에 배치하여 정확성을 확보하십시오.
안전한 오프셋 관리는 카프카 파이프라인의 데이터 정합성과 장애 복원력을 결정합니다. 위 전략을 바탕으로 서비스 특성에 맞는 커밋 정책을 설계해 보세요.
728x90
'개발 인프라 > 카프카' 카테고리의 다른 글
| 카프카 데드 레터 큐(DLQ) 패턴으로 카프카 메시지 처리 실패에 우아하게 대처하기 (0) | 2025.05.25 |
|---|---|
| 카프카 성능 최적화를 위한 기본 튜닝 포인트 (0) | 2025.05.23 |
| 카프카 프로듀서, 메시지 전송 보장 레벨(acks) (1) | 2025.05.23 |
| 카프카 메시지 직렬화, 무엇을 선택해야 할까 (0) | 2025.05.23 |
| 주키퍼(ZooKeeper)는 이제 안녕? KRaft 모드 알아보기 (0) | 2025.05.22 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- redis
- 카프카 개념
- ai통합
- 코프링
- 일급 객체
- First-class citizen
- 스브링부트
- vite
- 코틀린
- cqrs
- AI 에이전트
- JVM
- method Area
- JAVA 프로그래밍
- Heap Area
- MCP
- Stack Area
- 자바
- 언리얼엔진5
- 디자인패턴
- Java
- generated_body()
- model context protocol
- RESTfull
- unreal engjin
- 타입 안전성
- Subagent
- springai
- 언리얼엔진
- Claude Agent SDK
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
글 보관함
