역량 UP!/Architecture

Backoff Retry와 DLQ(Dead Letter Queue) 차이? 사용법?

태하팍 2025. 11. 11. 17:44
반응형

Backoff Retry와 DLQ 둘 다 재시도를 할 수 있는 친구들 입니다.
무슨 차이가 있고 언제 사용하면 좋은지 알아보고 사용하면 좋을것 같습니다.

Backoff Retry는

처리 실패시 일정 시간 대기(Backoff) 후 재시도하는 메커니즘 입니다.
예를 들면 네트워크 지연이나 일시적인 장애(DB lock,  외부 api timeout) 같은 복구 가능한 오류에 사용 됩니다.

동작 흐름

Consumer → 메시지 처리 실패 → 1초 대기 → 재시도  
→ 또 실패 → 3초 대기 → 재시도  
→ 또 실패 → 10초 대기 → 재시도 → 성공 or DLQ 이동

포인트

재시도 간격을 점점 늘려서 시스템 부하를 방지 합니다.

사용 예시

kafka Consumer / 외부 API 호출 실패 시 재시도

DLQ는

여러번 재시도 했음에도 계속 실패를 하면 별도의 큐(Dead Letter Queue)로 옮겨서 보관하는 방식 입니다.

동작흐름

Consumer → 메시지 처리 실패 → 3회 Backoff Retry → 여전히 실패 → DLQ 이동

포인트

재시도를 하거나 무시를 하거나 리포팅을 하여 수동복구를 할 수 있습니다.
주로 데이터 정합성 문제, 포맷 깨짐, 논리적 오류 같은 비복구성 문제를 처리 합니다.

사용 예시

DLQ(Dead Letter Queue)
Kafka에서는 DLQ역할을 하는 DLT(Dead Letter Topic)가 있음 : Consumer가 메시지 처리하다 실패 → Kafka Retry(재시도) → 그래도 실패 → Dead Letter Topic(실패한 메시지를 따로 모아두는 토픽)으로 메시지 전송 → 운영자나 별도 프로세스가 DLT를 모니터링하며 재처리

비교

구분 Backoff Retry DLQ
목적 일시적 오류 복구 지속 실패 메시지 격리
위치 Consumer 내부 로직 or 프레임워크 레벨 별도 Queue / Topic
재시도 횟수 제한 있음 (3~5회) 없음 (수동 조치)
트리거 네트워크, Timeout, 일시 장애 포맷 불일치, 비즈니스 로직 오류
예시 API 재시도, Kafka Retry(Spring Kafka) Kafka Dead-letter-topic, Amazon SQS DLQ

 

결론

Backoff Retry와 DLQ를 따로 생각하는것 보다 시도를 해보고 지속적인 실패를 했을 때 DLQ로 보낸다고 생각하면 좋을것 같습니다.
물론 로직 오류같은 경우 재시도 없이 DLQ로 바로 보내는 것도 생각해볼 수 있습니다.
상황에 맞게 잘 사용합시다:)

여기서 잠깐!

이제 Backoff Retry와 DLQ가 뭔지 알았습니다.
그럼 Kafka로 보면 재시도가 Producer / Consumer / Broker 3개의 레벨에서 동일하게 재시도가 가능한데 유의할 점이 있는가?

중복과 파티션 블락 유의!!




Producer : 네트워크, 브로커 전달 실패 복구 기능으로 재시도를 하며 옵션 중에 멱등성(
enable.idempotence)옵션을.
true로 설정하지 않으면 중복이 발생 합니다.
ex) Producer → Broker : 전송 성공!
      Broker → Producer : ack 응답 실패(네트워크 끊김) → 실패 →같은 메시지 다시 전송!!(중복 발생)

Consumer: 메시지를 받아서 처리하는데 있어서 실패가 나거나 예외가 발생하면 일시적인 장애는 Backoff 재시도를 통해 해결하며 
지속적인 실패를 한다면(영구 오류) DLQ로 격리 시킵니다.

Broker는 offset commit이 안 된 메시지를 다시 전달 합니다.(카프카 기본동작) 
→ 다시 말해 commit 안 된 레코드는 같은 파티션에 다시 전달 됩니다.
→ 한 레코드가 계속 실패하면 다음껄 실행하는게 아니라 해당 파티션이 막힙니다.(다음꺼 수행 X) 
그래서 Consumer쪽에서 Backoff Retry + DLQ가 필수 입니다.
DLQ로 보내면 처리가 계속 돌아갑니다.

각 레벨의 책임과 목적이 있으며 중복이나 파티션이 블락되지 않도록 재시도 정책(횟수, 간격, DLQ연계)을 설계하는게 중요 합니다.

메세지 순서를 보장해야하는 시스템이라면? (예: 주문 → 결제 →배송)
DLQ로 보내고 다음 메시지를 계속 처리하려고 보니..순서가 보장이 되어서 비즈니스 정합성이 유지되어야한다면??
이처럼 전략적으로 생각하고 설계를 해야합니다.

순서보다 처리 지속성이 중요한 경우
즉, 한두 건 실패해도 전체 처리는 돌아야하는 경우
로그성 이벤트나 통계집계같은 순서가 중요하지 않은 시스템

순서 보장이 최우선인 경우
앞 메세지가 실패면 뒤 메세지 처리도 중단 해야하는 경우
즉, 순서와 정합성이 100% 보장되어야 하는 경우
금융쪽이나 커머스쪽 분산 트랜젝션을 사용해서 정합성을 유지

결론적으로..
Kafka에서 순서를 보장해야하는 경우, 실패 메세지를 DLQ로 보내더라도 offset을 커밋하지 않으면 파티션이 블락됩니다.
순서를 보장하기 위해선 다음 메세지를 처리하지 않는게 맞으며 순서보다 처리량이 중요할 땐 DLQ로 보내면서 offset을 커밋하여
지속성을 유지 합니다.

그런데 "파티션 블락" 으로 인해 메시지가 엄청 쌓일텐데 괜찮은가??
순서를 지키기위해서는 대기하는게 맞습니다. 그리고 아래처럼 키 기반의 파티셔닝(aggregate-key)을 통해 영향 범위를 최소화 합니다.
파티션 키를 계좌ID/주문ID 등으로 잡아서 문제가 발생한 키만 해당 파티션에서 대기하게 합니다.
또한 나머지 파티션은 정상 처리 됩니다.

 

 

DLQ로 메시지를 넘긴 후 반드시 offset을 커밋 합니다. (커밋 정보는 _consumer_offsets이름의 내부 토픽에 저장됨.)

 

굳이 DLQ를 써야하나?? ELK같은데 오류 보내서 확인하거나 알람을 보내거나 하면 안되는건가??

DLQ를 쓰는 가장 큰 특징 중에 하나는 "재처리" 입니다.
코드버그나 데이터 이슈(정합성, 스키마, 누락 필드 등)로 인한 오류라면 핫픽스 후 DLQ에 모아둔 메세지를 정상처리 할 수 있습니다.

참고: https://docs.spring.io/spring-kafka/reference/kafka/annotation-error-handling.html
끝~

 

반응형