RabbitMQ Dead Letter Exchange 가이드
Dev
마지막 업데이트

RabbitMQ Dead Letter Exchange 가이드


RabbitMQ에서 dead letter exchange를 설정하면 많은 팀이 “에러 메시지가 마지막으로 모이는 안전한 통”처럼 생각합니다. 하지만 실제 incident에서는 더 앞단에서 막히는 경우가 많습니다. 왜 원래 queue를 떠났는지, 그리고 그다음에 정확히 어디로 라우팅돼야 하는지가 먼저 정리되지 않기 때문입니다.

핵심은 간단합니다. 먼저 dead-letter trigger를 확인하고, 그 동작이 policy인지 queue argument인지 확인한 다음, 마지막에 DLX route를 디버깅해야 합니다. 많은 DLX incident는 routing bug라기보다 control-plane 오해에서 시작합니다.


trigger와 route를 먼저 분리해서 보세요

dead-letter incident에는 사실 두 가지 질문이 있습니다.

  • 왜 메시지가 원래 queue를 떠났는가
  • 그다음 어디로 가야 했는가

많은 팀이 binding부터 보고 trigger를 놓칩니다. 그래서 expiration 문제를 nack 문제처럼 보고, policy 문제를 exchange 문제처럼 보는 일이 생깁니다.

dead lettering은 어떤 경우에 발생하나

RabbitMQ 문서를 보면 dead lettering은 rejection, expiration, queue length 관련 이벤트 등 여러 trigger로 발생할 수 있습니다.

즉 dead lettering은 하나의 사건이 아니라 여러 원인을 가진 사건 묶음입니다. 그리고 이 원인 차이가 다음에 어디를 봐야 하는지를 결정합니다.

실무적으로는 이렇게 생각하면 편합니다.

  • rejection과 nack는 consumer handling 쪽
  • expiration은 TTL과 timing 쪽
  • queue length limit은 backlog나 policy 쪽
  • quorum delivery-limit은 queue type 특화 동작 쪽

하드코딩한 x-arguments보다 policy를 먼저 보세요

RabbitMQ는 가능하면 하드코딩한 x-arguments보다 policy 사용을 권장합니다.

이게 중요한 이유는 많은 팀이 queue declaration에 dead-letter 동작을 박아 넣고, 이후 환경별 차이나 운영 변경을 추적하기 어려워지기 때문입니다. production과 staging이 다르게 움직이면 가장 먼저 볼 곳 중 하나가 여기입니다.

queue 선언만 보고 그게 전부라고 생각했는데 실제로는 policy가 덮어쓰고 있거나 보완하고 있는 경우도 흔합니다.

메시지가 예상한 곳으로 가지 않는 이유

대표적인 이유는 아래와 같습니다.

  • dead-letter exchange는 맞게 잡혀 있지만 routing-key 가정이 틀림
  • 의도한 policy가 queue에 실제 적용되지 않음
  • expiration 경로가 rejection 경로와 다름
  • quorum queue delivery-limit이나 queue-length 규칙이 개입됨

그래서 route를 보기 전에 trigger부터 이해해야 합니다.

흔한 troubleshooting 패턴

1. 메시지는 expire되는데 예상한 큐로 안 간다

trigger는 맞았지만 routing 가정이 틀린 것입니다.

2. reject된 메시지가 main queue를 떠난 뒤 사라진다

DLX 목적지나 binding이 팀이 생각한 것과 다릅니다.

3. 환경마다 동작이 다르다

policy와 하드코딩한 queue arguments가 서로 맞지 않습니다.

4. 실제 원인은 queue length나 delivery-limit이다

TTL이나 nack만 보고 있었지만 결과를 만든 것은 policy일 수 있습니다.

실전 점검 순서

1. 왜 dead-letter 되었는지 확인합니다

binding보다 trigger부터 봐야 합니다.

2. policy인지 queue argument인지 확인합니다

다른 설정 소스를 보고 있으면 이후 조사도 계속 어긋납니다.

3. DLX binding과 routing-key 가정을 확인합니다

trigger와 설정 출처가 분명해진 뒤에 route를 봐야 합니다.

4. 환경 간 동작을 비교합니다

한 환경에서만 이상하다면 policy drift 가능성이 큽니다.

5. queue length, TTL, delivery-limit 규칙을 확인합니다

실제 원인이 policy인데 nack 로직만 오래 들여다보는 경우가 많습니다.

바로 확인할 명령

rabbitmqctl list_queues name arguments
rabbitmqctl list_bindings
rabbitmqctl list_queues name messages_ready messages_unacknowledged

이 명령으로 DLX arguments, route, dead-letter queue 증가 속도를 같이 볼 수 있습니다.

시간을 아껴주는 간단한 sanity check

무엇을 바꾸기 전에 기대 경로를 한 문장으로 적어보세요.

  1. 메시지는 trigger X 때문에 queue A를 떠난다
  2. exchange B로 publish된다
  3. binding C를 통해 queue D로 간다

팀이 이 경로를 명확히 설명하지 못하면 아직 튜닝을 시작할 단계가 아닙니다. dead-letter 혼란의 상당수는 기대 control path를 먼저 적어보는 것만으로도 정리가 됩니다.

DLX incident를 볼 때의 관점

가장 유용한 질문은 “왜 dead-letter queue가 커졌지?”보다 “어떤 failure policy가 메시지를 여기로 보냈지?”입니다.

이 관점이 좋은 이유는 DLX가 보통 다른 제어 메커니즘의 결과이기 때문입니다.

  • rejection과 nack 동작
  • message expiration
  • queue length limit
  • quorum delivery limit이나 policy-driven handling

어떤 제어 메커니즘이 먼저 작동했는지 알면 route는 훨씬 쉽게 이해됩니다.

FAQ

Q. dead letter exchange는 reject된 메시지에만 쓰나요?

아니요. expiration이나 queue policy 이벤트도 dead lettering을 만들 수 있습니다.

Q. queue arguments와 policy 중 무엇이 더 좋은가요?

운영 변경과 비교가 쉬운 policy를 RabbitMQ가 권장합니다.

Q. 가장 빠른 첫 단계는 무엇인가요?

실제 trigger를 먼저 확인하고, 그 동작이 policy인지 queue argument인지 확인하는 것입니다.

Q. 메시지가 main queue를 떠난 뒤 사라져 보이는 이유는 무엇인가요?

대개 DLX route, binding, policy가 팀이 기대한 것과 다르기 때문입니다.

Sources:

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.