Spring Kafka 비차단 재시도: 지연 문제 진단 및 해결
Spring Boot 3.2와 Spring Kafka 3.1에서 @RetryableTopic을 사용해 Kafka 비동기 재시도를 구현할 때, 부하 하에서 재시도 토픽에 지연이 발생합니다. 메인 토픽은 메시지를 고르게 처리하지만, retry-0 토픽은 10~12분 동안 메시지를 축적한 후 부분적으로 정리되고 다시 멈추는 현상이 발생합니다. 이로 인해 처리 불균형이 생깁니다.
메트릭은 일관된 폴링 대신 간헐적인 패치 요청 급증을 보여줍니다. 컨슈머는 주기적으로 브로커에 하트비트를 위해 폴링하지만, 실제 읽기는 높은 지연 시간과 함께 불규칙하게 발생합니다.
분석을 위한 핵심 메트릭
진단을 위해 다음 Spring Kafka 메트릭을 수집하세요:
kafka_consumer_time_between_poll_avg/max— 폴링 간격.kafka_consumer_last_poll_seconds_ago— 마지막 폴링 이후 경과 시간.kafka_consumer_poll_idle_ratio_avg— 유휴 폴링 비율.kafka_consumer_fetch_manager_fetch_rate/fetch_latency_avg— 패치 속도 및 지연 시간.spring_kafka_listener_seconds_count/sum— 리스너 처리 시간.
패치 속도와 리스너 호출 그래프를 통해 확인할 수 있습니다: 처리가 일시적으로 활성화되고, 폴링 유휴 시간이 높습니다.
스레드 덤프를 통한 진단
스레드 덤프에서 ScheduledThreadPoolExecutor$DelayedWorkQueue.take에서 TIMED_WAITING 상태인 약 180개의 스레드가 발견되었습니다. 이는 지연된 파티션 재개에 사용되는 스케줄러 과부하를 나타냅니다.
RetryableTopic의 일시 중지 메커니즘
@RetryableTopic은 ContainerPartitionPausingBackOffManager를 사용합니다: 첫 번째 재시도 메시지 처리 시 백오프를 확인하고, 파티션을 현재 시간 + 지연 시간 동안 일시 중지한 후 ListenerContainerPauseService를 통해 재개를 예약합니다.
일시 중지 코드:
private final TaskScheduler scheduler;
public void pausePartition(MessageListenerContainer messageListenerContainer,
TopicPartition partition,
Duration pauseDuration) {
Instant resumeAt = Instant.now().plusMillis(pauseDuration.toMillis());
messageListenerContainer.pausePartition(partition);
this.scheduler.schedule(() -> {
messageListenerContainer.resumePartition(partition);
}, resumeAt);
}
높은 부하 하에서 스케줄러 작업 큐가 넘칩니다. 기본적으로 spring.task.scheduling.pool.size=1로 설정되어 병목 현상을 일으킵니다: 재개 작업이 누적되고, 패치가 제때 시작되지 않습니다.
해결책: 스케줄러 풀 크기 증가
application.properties에서 spring.task.scheduling.pool.size=10으로 설정하세요.
결과:
- Retry-0 지연이 허용 가능한 수준으로 안정화되었습니다 (백오프로 인해 0으로 떨어지지 않음).
- 폴링 간격이 짧아지고, 패치 속도가 증가했습니다.
- 리스너 호출이 일관성을 유지했습니다.
수정 후 지연 그래프: 증가 없이 평평하게 유지됩니다. 패치 지연 시간이 감소하고, 폴링이 더 빈번해졌습니다.
핵심 요점
- 제한된 스케줄러: 기본 1개의 스레드 풀은 재시도 부하 하에서 대량의 일시 중지/재개를 처리할 수 없습니다.
- 메트릭 우선: 스레드 덤프 전에 패치 속도, 폴링 유휴 시간, 리스너 시간을 모니터링하세요.
- 트랜잭션 부차적:
ack-mode=record,max.poll.records=1은 지연에 영향을 미치지 않습니다. - 동시성 OK: Spring Kafka는 파티션별로 올바르게 확장되며, 토픽이 증가해도 문제없습니다.
- 대안: 고부하 환경에서는 일시 중지 대신 타임스탬프 확인 루프를 사용한 맞춤형 재시도를 고려하세요.
모니터링 권장사항
다음 대시보드를 추가하세요:
- 정확히 한 번 처리를 위한 커밋/롤백 속도.
- 스케줄러 큐 크기 (맞춤형 메트릭).
- 파티션 일시 중지/재개 이벤트.
- 백오프 타임스탬프와 현재 시간 차이.
프로덕션 전에 부하 테스트를 수행하세요. Spring Kafka 소스 코드는 전체 메커니즘에 대한 통찰력을 제공합니다.
— Editorial Team
아직 댓글이 없습니다.