[RabbitMQ] Dead Letter Exchange & TTL(Time To Live)

반응형

이번 글에서는 RabbitMQ의 Dead Letter Exchange와 TTL(Time to Live)에 대해 알아보겠습니다.

1. Dead Letter Exchnage란?

처리할 수 없는 Message를 전달할 Exchange입니다.

기본적으로 RabbitMQ의 메세지가 예상치 못한 error로 인해 처리될 수 없을 경우 다시 Queue로 돌아가는 requeuing을 수행하게 됩니다. 하지만, 수차례 동일한 에러로 인해 해당 메시지를 처리할 수 없을 경우에도 이 메세지는 무한히 Queue에 담겨있게 됩니다. 마찬가지로 동일한 에러가 무한히 발생하게 됩니다.

이를 방지하고자 특정 횟수 이상의 error가 발생한 메세지는 Dead Letter Exchange로 보내 적절한 error handling 과정을 거치도록 설계해야 합니다. 예를들어 DLX에 메세지가 전달된 경우 admin에게 알람을 보내도록 설계할 수 있습니다.

마찬가지로 특정 Queue에 일정시간 이상 머문 메세지 또한 DLX로 보내 error handling 과정을 거치도록 설계 할 수 있습니다.

전체 구조는 아래와 같습니다.

image.png

Exchange는 아래와 같습니다.

image.png

Queue는 아래와 같습니다.

image.png

위와 같은 에러 메세지를 DLX로 전달하는 방법은 두가지 방법을 고려할 수 있습니다.

1-1) AmqpRejectAndDontRequeueException

첫번째 방법은 Spring boot에 구현되어있는 AmqpRejectAndDontRequeueException를 이용하는 방법입니다.

소스코드에서는 에러가 발생할 경우 아래와 같이 AmqpRejectAndDontRequeueException Exception을 발생시켜, 해당 메세지를 기존의 Queue가 아닌 DLX로 전달할 수 있습니다.

@Service
public class MyPictureConsumer {

    private Logger log = LoggerFactory.getLogger(MyPictureConsumer.class);

    ObjectMapper objectMapper = new ObjectMapper();

    @RabbitListener(queues = "q.mypicture.image")
    public void listen(String message) throws IOException {

            Picture p = objectMapper.readValue(message , Picture.class);

            if(p.getSize()>9000){
                throw new AmqpRejectAndDontRequeueException("Picture size is too large " + p);
            }

            log.info("Image Picture is {} ", p);

    }
}

1-2) Manual Rejection

두번째 방법은 RabbitMQ의 MessageChannel을 사용해 직접 manual reject process를 구현하는 방법입니다.

여기서 Channel은 Java와 RabbitMQ 사이에서 Message가 이동하는 Tunnel이라고 생각하면 됩니다.

image.png

먼저 application.yml 파일을 열어 아래와 같은 config를 추가해줍니다.

spring:
  rabbitmq:
    listener:
      direct:
        acknowledge-mode: manual
      simple:
        acknowledge-mode: manual

다음으로 Consumer에서 기존의 String으로 받았던 RabbitListener의 파라미터 값을 Message와 Channel로 변경합니다. manual reject process를 구현하기 위해선 Channel을 통해 ack/reject를 직접 rabbitmq에 알려줘야합니다.

또한, 특정 메시지에 대한 ack/reject를 rabbitmq에 통보하기 위해 위해 메세지의 고유 ID값인 Delivery Tag를 key값으로 전달합니다.

@Service
public class MyPictureConsumer {

    private Logger log = LoggerFactory.getLogger(MyPictureConsumer.class);

    ObjectMapper objectMapper = new ObjectMapper();

    @RabbitListener(queues = "q.mypicture.image")
    public void listen(Message message, Channel channel) throws IOException {

            Picture p = objectMapper.readValue(message.getBody() , Picture.class);

            if(p.getSize()>9000) {
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
            }

            log.info("Image Picture is {} ", p);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

😎.. Which one?

주의점으로는, 위의 두가지 방법은 동시에 사용할 수 없다는 것 입니다.

Spring Boot의 reject과 Manual로 구현한 reject은 한 프로젝트 내에서 같이 사용할 수 없습니다. manual reject을 구현하기위해선 application.yml을 수정하기 떄문에 전체 프로젝트 config에 영향을주기 때문입니다. 개발자는 둘 중 한가지 방법을 선택해 구현하면 되겠습니다.

2. TTL(Time to Live)란?

메세지가 특정 Queue에 머물 수 있는 시간입니다.

만약 특정 메세지가 Queue에 특정 시간 이상 머문경우, 이때는 Consumer가 제대로 메세지를 제대로 처리할 수 없는 경우로 판단할 수 있습니다. 따라서 이때는 해당 메세지를 Dead Leeter Exchange로 보내어 적절한 error handling 과정을 거치도록 설계할 수 있습니다.

각 Queue별로 TTL 시간 셋팅값은 각 어플리케이션 환경별로 개발자가 셋팅할 수 있습니다.

3. 정리

정리하자면? 🤣

  1. Consumer가 메시지를 처리할 때 지속적으로 에러가 발생하거나
  2. 너무 오랫동안 Queue에 처리되지 않은 상태로 남아있는 메세지는

-> "DLX(Dead Letter Exchange)로 보내 에러 처리 프로세스를 거친다" 라고 요약할 수 있겠습니다.


참고 자료 : https://www.udemy.com/course/rabbitmq-java-spring-boot-for-system-integration/


추천서적

 

RabbitMQ 따라잡기:AMQP 기반의 오픈소스 메시지 브로커

COUPANG

www.coupang.com

파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음


반응형

'RabbitMQ' 카테고리의 다른 글

[RabbitMQ] Publisher API  (0) 2020.08.22
[RabbitMQ] Retry Mechanism  (0) 2020.08.22
[RabbtiMQ] Exchange (Message broker)  (0) 2020.08.22
[RabbitMQ] JSON Message Format 사용하기 - 2  (0) 2020.08.22
[RabbitMQ] JSON Message Format 사용하기 - 1  (0) 2020.08.22

댓글

Designed by JB FACTORY