본문 바로가기
1. 개발/1.8. ActiveMQ

미전달 메시지(DLQ)로 보내기 전 '최대 재시도 횟수'의 기본값과 변경법은?

by 엉짱 2026. 3. 16.
반응형

미전달 메시지(DLQ)로 보내기 전 '최대 재시도 횟수'의 기본값과 변경법은?

메시지 브로커(Message Broker) 환경에서 컨슈머(Consumer)가 메시지를 처리하는 도중 데이터베이스 장애나 외부 API 타임아웃 등으로 인해 예외(Exception)가 발생할 수 있습니다. 이때 브로커는 해당 메시지가 유실되지 않도록 컨슈머에게 다시 전달하는 재전송(Redelivery) 메커니즘을 작동시킵니다.

하지만 영원히 재시도할 수는 없습니다. 복구 불가능한 형태의 데이터(Poison Pill)라면 브로커의 자원만 낭비하고 전체 큐의 처리를 멈추게 만들기 때문입니다. 지정된 '최대 재시도 횟수'를 초과하여 실패한 메시지는 최종적으로 격리 공간인 DLQ(Dead Letter Queue)로 이동하게 됩니다.

이 가이드에서는 ActiveMQ "Classic" 버전과 차세대 아키텍처인 ActiveMQ Artemis 환경에서 최대 재시도 횟수의 기본값이 어떻게 다르고, 이를 어떻게 변경하는지 상세히 분석합니다.


1. ActiveMQ "Classic"의 기본값과 변경법 (클라이언트 측 제어)

ActiveMQ Classic(5.x 버전)의 가장 큰 특징은 메시지 재전송 정책(RedeliveryPolicy)의 주도권이 브로커가 아닌 컨슈머 클라이언트(Client)에 있다는 점입니다.

  • 기본값 (Default): 6회
  • 동작 방식: 최초 1회 전달 시도 후 처리에 실패(Rollback 또는 Session Recover 호출)하면, 클라이언트 내부 라이브러리가 브로커에 처리를 유보하고 총 6번을 더 재시도합니다. (총 7회 시도). 6번의 재시도마저 모두 실패하면, 클라이언트는 브로커에게 "이 메시지는 독성 메시지(Poison Ack)다"라고 통보하며 브로커가 이를 ActiveMQ.DLQ라는 기본 큐로 보냅니다.

설정 변경 방법:
Spring Boot 환경이나 Java 클라이언트 코드에서 ActiveMQConnectionFactory 객체의 RedeliveryPolicy 속성을 수정하여 제어합니다.

import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.RedeliveryPolicy;

// 1. ConnectionFactory 생성
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");

// 2. RedeliveryPolicy 인스턴스 생성 및 설정
RedeliveryPolicy policy = new RedeliveryPolicy();
policy.setMaximumRedeliveries(3); // 최대 재시도 횟수를 3회로 변경 (0으로 설정 시 재시도 안 함)
policy.setInitialRedeliveryDelay(1000); // 최초 재시도 전 대기 시간 (1초)

// 3. 팩토리에 정책 적용
factory.setRedeliveryPolicy(policy);

이러한 클라이언트 측 제어는 각 애플리케이션의 특성에 맞춰 재시도 횟수를 유연하게 가져갈 수 있다는 장점이 있지만, 여러 이기종 클라이언트가 접속할 경우 DLQ 정책이 파편화된다는 단점이 존재합니다.


2. ActiveMQ Artemis의 기본값과 변경법 (브로커 측 제어)

차세대 브로커인 ActiveMQ Artemis는 아키텍처의 안정성을 위해 재전송 제어의 주도권을 클라이언트에서 서버(Broker)로 가져왔습니다. 클라이언트가 실패 응답을 보내면, 브로커가 직접 재시도 횟수를 카운트하고 DLQ 이동을 통제합니다.

  • 기본값 (Default): 10회
  • 동작 방식: 메시지가 큐에서 컨슈머로 배달(Delivery)될 때마다 브로커는 내부적으로 delivery-count를 증가시킵니다. 이 카운트가 브로커에 설정된 max-delivery-attempts 값을 초과하면 해당 메시지는 설정된 DLQ(보통 DLQ 주소)로 즉시 라우팅됩니다.

설정 변경 방법:
클라이언트 코드를 수정할 필요 없이, 브로커 서버의 broker.xml 설정 파일에서 <address-setting> 블록을 수정하여 전역적 혹은 특정 큐 단위로 제어합니다.

<address-settings>
   <address-setting match="orders.queue">
      <max-delivery-attempts>5</max-delivery-attempts>
      <dead-letter-address>DLQ</dead-letter-address>
      <redelivery-delay>2000</redelivery-delay>
   </address-setting>

   <address-setting match="#">
      <max-delivery-attempts>10</max-delivery-attempts>
      <dead-letter-address>DLQ</dead-letter-address>
   </address-setting>
</address-settings>

Artemis의 방식은 중앙 집중적인 관리가 가능하여, 마이크로서비스 아키텍처(MSA)와 같이 다양한 클라이언트가 접속하는 환경에서 전체 시스템의 데이터 일관성과 큐의 안정성을 보장하는 데 훨씬 유리합니다.


3. 재시도 횟수 설정 시 핵심 아키텍처 고려사항

단순히 숫자를 늘리거나 줄이는 것을 넘어, 시스템의 장애 전파를 막기 위해 다음 세 가지를 반드시 설계에 반영해야 합니다.

  1. 무한 재시도(-1)의 위험성 (Head-of-Line Blocking):
    최대 재시도 횟수를 무한대(-1 또는 아주 큰 수)로 설정하는 것은 안티 패턴입니다. 특정 파싱 에러나 NullPointerException을 유발하는 메시지가 큐의 최상단에 걸려있을 경우, 이 하나의 메시지를 영원히 재시도하느라 뒤에 쌓인 정상적인 수만 개의 메시지들이 전혀 처리되지 못하는 선두 차단(Head-of-Line Blocking) 현상이 발생합니다. 적절한 횟수(예: 3~5회) 이후에는 과감히 DLQ로 빼내어 격리해야 합니다.
  2. 지수 백오프 (Exponential Backoff) 활용:
    DB 락(Lock)이나 외부 API 서버의 일시적인 다운은 1~2초 만에 복구되지 않을 수 있습니다. 실패 즉시 재시도하기보다는, 재시도 간격을 기하급수적으로 늘려가는(예: 1초 $\rightarrow$ 2초 $\rightarrow$ 4초 $\rightarrow$ 8초) 지수 백오프 옵션(useExponentialBackOff=true 또는 redelivery-multiplier)을 적용하여 외부 시스템이 회복할 시간을 벌어주어야 합니다.
  3. 소비자의 멱등성 (Idempotency) 확보:
    네트워크 지연으로 인해 컨슈머가 메시지를 정상 처리하고도 브로커에게 ACK를 늦게 보내면, 브로커는 실패로 간주하고 동일한 메시지를 다시 전송할 수 있습니다. 최대 재시도 횟수 내에서 메시지는 언제든 중복 수신될 수 있으므로, 비즈니스 로직은 같은 메시지를 여러 번 처리해도 최종 데이터 상태가 동일하게 유지되도록 방어적으로 설계되어야 합니다.
반응형