RabbitMQ와 Kafka의 DLQ 비교: 설정·전달 보장·재처리

anlee·2025년 5월 6일

매일매일 블로그

목록 보기
6/49

메시지 처리에 실패했을 때 무한 재시도만 하면 같은 메시지가 자원을 점유하고 뒤의 작업도 막을 수 있다. Dead Letter Queue 또는 Dead Letter Topic은 이런 메시지를 분리해 원인을 확인하고 재처리하기 위한 장소다.

RabbitMQ에서는 DLX가 큐 사이의 재라우팅을 담당하고, Kafka에서는 사용 중인 소비자·Connect·Streams·프레임워크에 따라 실패 메시지 처리 방식이 달라진다. 설정이 간단한지와 전달이 어떤 조건에서 보장되는지는 따로 비교해야 한다.

1. RabbitMQ: DLX는 교환기, DLQ는 받는 큐

RabbitMQ의 Dead Letter Exchange는 실패 메시지를 다시 라우팅하는 exchange다. DLQ는 그 exchange에 연결한 목적지 큐다. 소비자가 nack/reject하면서 requeue를 false로 지정하거나, 메시지 TTL·큐 길이 제한 등 해당 조건에 도달했을 때 dead-lettering이 발생할 수 있다. quorum queue의 delivery limit도 관련되며 세부 동작은 버전에 맞춰 확인한다.

업무 exchange → 업무 queue → 소비자 처리
                    |
            dead-letter 조건 발생
                    ↓
                   DLX → DLQ

requeue=true로 계속 되돌리면 이 흐름과 다르게 같은 큐에서 반복 처리될 수 있다. DLX 이름과 라우팅 키를 지정해도 실제 exchange·queue·binding이 없거나 맞지 않으면 의도한 목적지로 가지 않는다.

설정은 가능하면 policy로 관리한다. 하드코딩한 x-dead-letter-exchange 같은 queue argument는 변경하려면 큐 재선언 등 운영 절차가 필요할 수 있다. 애플리케이션 재배포 없이 조정할 설정과 큐 생성 시 정하는 설정을 구분한다. RabbitMQ Dead Letter Exchanges

2. 기본 dead-lettering을 유실 없는 전송으로 보지 않기

기본 DLX 재발행은 내부 publisher confirm 없이 이루어질 수 있다. 대상 큐가 메시지를 받을 수 없는 상황에서는 원본에서 제거됐지만 목적지에는 도착하지 않는 위험이 있다. “RabbitMQ가 자동으로 DLQ로 보내므로 안전하다”는 설명은 이 조건을 놓친다. RabbitMQ DLX Safety

quorum queue에서 지원하는 at-least-once dead-lettering을 사용하려면 원본 큐 유형과 관련 정책을 맞춰야 한다. 아래는 정책의 핵심 키를 보여 주는 예다. 큐·exchange·binding을 생성하는 전체 배포 설정은 아니다.

{
  "dead-letter-exchange": "orders.dlx",
  "dead-letter-routing-key": "orders.failed",
  "dead-letter-strategy": "at-least-once",
  "overflow": "reject-publish",
  "max-length": 10000
}

원본이 quorum queue여야 하고, dead-letter-strategy=at-least-once, overflow=reject-publish, DLX 설정이 필요하다. 공식 문서의 stream_queue feature flag 조건도 확인한다. classic queue에 같은 policy를 붙이는 것으로 동일한 동작을 얻을 수는 없다. drop-head를 유지하면 의도한 at-least-once 조건과 맞지 않는다.

목적지에서 수신 확인을 받지 못한 메시지는 원본에 남아 자원을 사용하고 재시도될 수 있다. 따라서 용량·적체·중복 수신을 관리해야 한다. 목적지의 재시작까지 고려하면 큐의 durability와 메시지 persistence도 맞춰야 한다. at-least-once는 중복이 없다는 뜻이 아니다. RabbitMQ Quorum Queue의 dead-lettering 조건

3. Kafka: 어떤 구성요소를 말하는지 먼저 구분하기

구성요소실패 메시지 처리 범위
Kafka의 기본 producer·consumer애플리케이션의 DLT 발행·offset 처리 설계가 필요
Kafka ConnectSink 경로의 오류 허용·DLQ 옵션과 커넥터 지원 범위 확인
Kafka Streams사용하는 버전의 역직렬화·처리·생산 예외 핸들러와 지원 기능 확인
Spring Kafka오류 핸들러·recoverer·트랜잭션 구성에 따라 재시도와 DLT 처리

한 구성요소의 옵션을 “Kafka 전체가 지원한다”거나 “어떤 경우에도 직접 구현해야 한다”로 넓히지 않는다. 특히 새 버전의 Streams 기능과 이전 버전의 예외 핸들러 예제를 섞지 않는다.

Kafka Connect 4.0 문서의 Sink DLQ 설정 예는 다음과 같다.

errors.tolerance=all
errors.deadletterqueue.topic.name=orders-connect-dlq
errors.deadletterqueue.context.headers.enable=true

이 옵션을 Source·Sink 공통의 모든 오류 처리로 설명하면 안 된다. 변환·역직렬화 등 처리 단계와 커넥터가 보고하는 오류의 범위를 확인한다. 연결 장애나 모든 외부 시스템 오류가 레코드 단위 DLQ로 전환되는 것도 아니다. Kafka Connect 4.0 설정

Kafka Connect Sink 처리에서 실패 레코드를 분리하는 예

이 그림의 source topic은 Kafka에서 읽는 입력 토픽을 뜻한다. 그림이 Kafka Connect Source 커넥터의 모든 실패에 DLQ 옵션을 제공한다는 의미는 아니다.

4. DLT 발행과 offset 커밋의 순서

일반 소비자에서 다음 순서를 생각해 볼 수 있다.

메시지 처리 실패
→ 재시도 정책 적용
→ 재시도 한도 초과 시 DLT 발행
→ DLT 발행 성공 확인
→ 원본 offset 진행

DLT 발행 결과를 확인하기 전에 offset부터 커밋하면 실패 메시지를 잃을 수 있다. 반대로 DLT 발행 후 offset 커밋 전에 죽으면 원본을 다시 읽어 DLT에 중복 발행할 수 있다. 같은 파티션의 뒤 offset을 커밋하면 앞의 실패 레코드도 처리된 것으로 넘어갈 수 있으므로 비동기·병렬 처리의 커밋 순서도 중요하다.

Kafka 트랜잭션으로 DLT 발행과 소비 offset을 묶는 설계를 사용할 수 있지만, producer·consumer 격리 설정과 오류 처리 경로가 맞아야 한다. 그 범위를 외부 DB·메일·HTTP 호출까지 확장해서 “정확히 한 번”으로 설명하지 않는다.

Spring Kafka에서는 DeadLetterPublishingRecoverer와 오류 핸들러를 사용할 수 있다. DLT 발행 실패 시 복구가 성공한 것으로 처리되지 않도록 send 결과와 예외 전파 설정을 확인한다. 설정만 붙였다는 이유로 유실·중복·순서 테스트를 생략하지 않는다. Spring Kafka 오류 처리

5. 운영 관점에서 비교하기

관점RabbitMQKafka
기본 모델큐에서 전달·ACK하며 DLX로 재라우팅로그에서 읽고 offset으로 진행 위치 관리
실패 분리큐 유형·DLX·policy 조합소비자·프레임워크·Connect 등의 처리 경로
재처리DLQ에서 업무 큐 등으로 재발행DLT 소비·재발행 또는 설계한 offset 재처리
주의할 보장기본 DLX 전송과 quorum at-least-once 구분DLT 발행 확인과 offset 커밋 경계

DLQ·DLT에 넣는 순간 운영 책임이 끝나는 것은 아니다. 담당자, 알림, 보관 기간, 재처리 승인 기준을 정한다. 원래 이벤트 ID와 스키마 버전은 보존하되, 오류 메시지와 헤더에 토큰·개인정보가 과도하게 쌓이지 않게 한다.

재처리 시에는 원인이 해결됐는지, 오래된 이벤트를 지금 적용해도 되는지, 중복 적용을 막을 수 있는지 확인한다. 실패한 메시지를 무조건 원래 위치로 되돌리면 같은 장애를 반복하거나 뒤의 이벤트보다 늦게 적용될 수 있다. DLQ가 편리한 도구가 되려면 분리 이후의 복구 절차까지 설계되어 있어야 한다.

0개의 댓글