정우님, 요청하신 대로 DLX → retry → backoff → 재큐잉 구조를 “간단한 말”이 아니라 정확한 RabbitMQ 내부 동작 기준으로 냉정하게 설명해드리겠습니다.
메시지 처리 실패 시 아래 흐름으로 순환하는 구조입니다:
[메인 큐] --(처리 실패/에러)--> [DLX 큐] --(지연/backoff)--> [재큐잉] --> [메인 큐 재시도]
즉:
이걸 흔히 retry with backoff 구조라고 한다.
큐에서 메시지가 아래 조건일 때 “죽은 편지(dead letter)”로 처리되어 다른 큐로 이동되는 기능:
즉, 실패 메시지를 관리하는 전용 경로.
main_queue:
x-dead-letter-exchange: retry.exchange
메인 큐에서 실패한 메시지는 모두 retry.exchange로 이동.
Retry 큐는 “지연큐”처럼 동작해야 한다. 즉:
예:
retry.queue (TTL=5000ms)
→ 메시지가 5초 동안 대기 (backoff)
→ TTL 종료 시 정의된 Dead-Letter Exchange로 자동 이동
지연큐의 핵심은 "소비자가 붙지 않는 큐"라는 것.
retry.queue:
x-message-ttl: 5000 # 5초 지연
x-dead-letter-exchange: main.exchange # TTL 끝나면 원래 메인 큐로 이동
→ 이렇게 하면 “자연스럽게 재큐잉(requeue)”가 이루어진다.
Retry할 때 즉시 재시도하면 안 되는 상황이 대부분이기 때문에
지연 시간을 추가하는 것을 backoff라고 한다.
예:
1차 실패 → 3초 후 재시도
2차 실패 → 10초 후 재시도
3차 실패 → 30초 후 재시도
이를 Exponential Backoff 라고 한다.
RabbitMQ에서 TTL로 구현 가능.
retry_3s.queue (TTL=3000)
retry_10s.queue (TTL=10000)
retry_30s.queue (TTL=30000)
그리고 실패 횟수(header)에 따라 다른 retry queue로 라우팅한다.
Retry Queue의 TTL이 끝나면 메시지 헤더가 유지된 채로
다시 원래 메인 큐로 자동으로 돌아가는 것.
즉, 소비자는 “새 메시지처럼” 수신하지만
내부적으로는 실패 후 재시도 중인 메시지.
[1] 소비자 처리 실패
↓ (Nack requeue=false)
[2] 메인 큐 → DLX로 이동
↓
[3] DLX가 가리키는 Retry Queue에 저장
↓ (x-message-ttl)
[4] TTL만큼 backoff (delay)
↓ (TTL 만료)
[5] Dead-letter exchange → 메인 큐 재투입
↓
[6] 소비자가 다시 처리
순환.
정우님처럼 대용량 FFmpeg 작업 처리에서는
5~10회 정도 retry 후 마지막에는 DLQ로 보내서 보관하는 게 맞음.
✔ 이 구조는 RabbitMQ에서 정석적인 재시도 패턴
✔ 특히 영상 인코딩, 문서 변환 등 “잠시 장애”가 있을 수 있는 작업에 매우 적합
❌ 하지만 이걸 구현하려면 큐와 익스체인지 설계를 명확히 해야 한다
❌ 그리고 CorrelationId와 MessageId를 분리해야 재시도 추적이 명확해진다
(정우님이 JobID를 CorrelationId로 쓰는 문제와 연결됨)
정우님 RabbitMQ 구조는 지금 어떻게 구성되어 있나요?
구체적인 큐 구성 알려주시면 정우님 상황에 맞는 정확한 retry 설계를 최적화해서 드릴게요.