RabbitMQ 메시지 재처리

백엔드&인프라 추종자·2025년 11월 15일

정우님, 요청하신 대로 DLX → retry → backoff → 재큐잉 구조를 “간단한 말”이 아니라 정확한 RabbitMQ 내부 동작 기준으로 냉정하게 설명해드리겠습니다.


✅ 0. 전체 그림 (가장 핵심 요약)

메시지 처리 실패 시 아래 흐름으로 순환하는 구조입니다:

[메인 큐] --(처리 실패/에러)--> [DLX 큐] --(지연/backoff)--> [재큐잉] --> [메인 큐 재시도]

즉:

  1. 메시지가 실패한다
  2. 실패 메시지를 DLX(dead-letter queue)로 보낸다
  3. DLX에서 TTL(지연) 적용
  4. TTL 끝나면 자동으로 원래 큐로 돌아감 (재시도)
  5. 특정 횟수 이상 실패하면 최종 실패 큐로 이동

이걸 흔히 retry with backoff 구조라고 한다.


✅ 1. DLX(Dead-Letter Exchange)란 무엇인가?

큐에서 메시지가 아래 조건일 때 “죽은 편지(dead letter)”로 처리되어 다른 큐로 이동되는 기능:

  • Nack + requeue=false
  • TTL 만료 (메시지 또는 큐 TTL)
  • max-length 초과

즉, 실패 메시지를 관리하는 전용 경로.


✔ 예: 메인 큐 정의

main_queue:
  x-dead-letter-exchange: retry.exchange

메인 큐에서 실패한 메시지는 모두 retry.exchange로 이동.


✅ 2. Retry Queue (지연 큐, backoff 적용)

Retry 큐는 “지연큐”처럼 동작해야 한다. 즉:

  • 메시지가 들어오면 그대로 소비되지 않음
  • TTL이 끝나야 다음 exchange로 이동

예:

retry.queue (TTL=5000ms)
  → 메시지가 5초 동안 대기 (backoff)
  → TTL 종료 시 정의된 Dead-Letter Exchange로 자동 이동

지연큐의 핵심은 "소비자가 붙지 않는 큐"라는 것.


✔ Retry Queue 설정 예

retry.queue:
  x-message-ttl: 5000              # 5초 지연
  x-dead-letter-exchange: main.exchange  # TTL 끝나면 원래 메인 큐로 이동

→ 이렇게 하면 “자연스럽게 재큐잉(requeue)”가 이루어진다.


✅ 3. Backoff란?

Retry할 때 즉시 재시도하면 안 되는 상황이 대부분이기 때문에
지연 시간을 추가하는 것을 backoff라고 한다.

예:

1차 실패 → 3초 후 재시도
2차 실패 → 10초 후 재시도
3차 실패 → 30초 후 재시도

이를 Exponential Backoff 라고 한다.

RabbitMQ에서 TTL로 구현 가능.


❗ Backoff를 여러 단계로 하고 싶으면 큐를 여러 개 둔다

retry_3s.queue (TTL=3000)
retry_10s.queue (TTL=10000)
retry_30s.queue (TTL=30000)

그리고 실패 횟수(header)에 따라 다른 retry queue로 라우팅한다.


✅ 4. 재큐잉(Requeue)란?

Retry Queue의 TTL이 끝나면 메시지 헤더가 유지된 채로
다시 원래 메인 큐로 자동으로 돌아가는 것.

즉, 소비자는 “새 메시지처럼” 수신하지만
내부적으로는 실패 후 재시도 중인 메시지.


전체 Flow 예 (정확한 RabbitMQ 방식)

[1] 소비자 처리 실패
      ↓ (Nack requeue=false)
[2] 메인 큐 → DLX로 이동
      ↓
[3] DLX가 가리키는 Retry Queue에 저장
      ↓ (x-message-ttl)
[4] TTL만큼 backoff (delay)
      ↓ (TTL 만료)
[5] Dead-letter exchange → 메인 큐 재투입
      ↓
[6] 소비자가 다시 처리

순환.


마지막 단계: 최종 실패 큐 (DLQ: Dead Letter Queue)

정우님처럼 대용량 FFmpeg 작업 처리에서는
5~10회 정도 retry 후 마지막에는 DLQ로 보내서 보관하는 게 맞음.

  • 메시지 loop 방지
  • 실패 원인 분석 용도

정우님에게 냉정하게 조언

✔ 이 구조는 RabbitMQ에서 정석적인 재시도 패턴
✔ 특히 영상 인코딩, 문서 변환 등 “잠시 장애”가 있을 수 있는 작업에 매우 적합
❌ 하지만 이걸 구현하려면 큐와 익스체인지 설계를 명확히 해야 한다
❌ 그리고 CorrelationId와 MessageId를 분리해야 재시도 추적이 명확해진다

(정우님이 JobID를 CorrelationId로 쓰는 문제와 연결됨)


정우님 RabbitMQ 구조는 지금 어떻게 구성되어 있나요?

  • 메인 큐 1개?
  • retry 큐 1개? 여러 개?
  • DLQ는 별도로 두었나요?

구체적인 큐 구성 알려주시면 정우님 상황에 맞는 정확한 retry 설계를 최적화해서 드릴게요.

profile
AI 답변 글을 주로 올립니다.

0개의 댓글