[RabbitMQ] nack/reject로 다른 큐로 보내기 (dead-letter)

by 스뎅(thDeng) on

RabbitMQ로 메시지를 처리하다 문제가 발생하면, 다음 메시지를 처리하기 위해 이 메시지를 건너뛰어야 하는 경우가 있다. 이 메시지를 건너뛰어야 꾸준히 들어오는 다음 메시지를 처리할 수 있기 때문이다. 일시적으로 다른 API 호출이나 연결 문제가 있는 메시지는 나중에 재시도 하면 정상처리 될 수 있기 때문에 문제가 되는 메시지는 잠시 다른 곳에 보관한다.

queue에 dead letter 설정을 해두고, nack나 reject를 보내면 된다. 이 때, requeue는 false로 설정해서 보내야 한다. (requeue를 true로 하면 dead letter로 가지 않고 바로 메시지가 있던 원래 queue로 돌아간다.)

참고로 RabbitMQ에서 dead letter로 처리되는 경우는 다음과 같은 조건이고, 여기서 소개하는 방법은 nack/reject를 사용 하는 것이다.:

샘플로 보자. 전체 샘플은 샘플로 만들어둔 코드에서 확인할 수 있다. 전체 샘플에 spring-amqp를 사용한 샘플도 있다. (큰 차이가 없어서 생략)

Map props1 = new HashMap();
props1.put("x-dead-letter-exchange", "x1");
channel.queueDeclare("q1", false, false, false, props1);

위처럼 queue에 dead letter 설정을 해준다. 그리고 consumer에서 nackreject를 보내면 x-dead-letter-exchange에 설정된 exchange로 보내지는 것을 확인할 수 있다.

DefaultConsumer consumer = new DefaultConsumer(channel) {
  @Override
  public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    String message = new String(body, "UTF-8");
    System.out.println(" [x] Received '" + message + "'");

    // requeue=false
    channel.basicNack(envelope.getDeliveryTag(), true, false);
    // channel.basicReject(envelope.getDeliveryTag(), false);
    System.out.println(" Message is rejected: " + message);
  }
};

주의점

auto ack는 false로 설정

auto acktrue로 설정되어 있으면, 메시지를 가져오면 자동으로 ack를 보내서 RabbitMQ 서버에서 메시지가 사라지게 된다. 메시지를 가져와서 처리가 끝날 때 수동으로 ack를 보낼 수 있도록 auto ackfalse로 설정하자. 메시지를 가져와서 ack를 보내기 전 까지는 이 메시지를 다른 컨슈머에게 주지도 않고, 큐에서 제거하지도 않는다. 정상적으로 처리가 되지 않을 때, ack 대신 nack를 보내면 된다.

여기서 auto ack는 spring-amqp의 acknowledgeMode와는 다른 개념이다. (https://docs.spring.io/spring-amqp/reference/htmlsingle/#containerAttributes)

requeue=false로 설정

nackreject를 보낼 때 requeue=false로 설정하지 않으면, 이 메시지는 큐의 원래 위치로 돌아가게 된다. 그러면 이 컨슈머나 혹은 다른 컨슈머가 그걸 다시 가져와서 처리하게 된다. 이렇게 다른 컨슈머가 바로 처리해도 되는 비즈니스가 맞는 곳이라면 dead letter를 설정하지 말고 그대로 requeue 하면 된다.

다른 컨슈머에게 패스하기(Negative Acknowledgement and Requeuing of Deliveries) 정도의 처리가 될 것 같다. :D

RabbitMQ에서 requeue는 가능한 메시지의 원래 위치에 넣게 된다. 만일 이미 많은 메시지가 빠져 나가서 원래 위치를 찾기 어려운 경우는 queue의 head에 가장 가까운 위치에 넣는다. (nack)

참고

별도로 명시하지 않을 경우, 이 블로그의 포스트는 다음 라이선스에 따라 사용할 수 있습니다: Creative Commons License CC Attribution-NonCommercial-ShareAlike 4.0 International License