티스토리 뷰

728x90
반응형

RabbitMQ를 사용하여 알림 발송하기 1편

 

Spring Boot - RabbitMQ를 사용하여 알림 발송 1편

ApplicationEventPublisher란? 이벤트 리스너는 발생된 event에 반응하고 이벤트 리스너는 발생된 event를 전달받아 이벤트에 담긴 데이터를 기반으로 특정한 기능을 수행합니다. 🤔 왜 ApplicationEventPublishe

kdg-is.tistory.com

 

지난 이야기


  • 지난 이야기에서는 RabbitMQ를 사용하여 특정 클라이언트가 회원가입을 완료했을 경우 알림을 보냈습니다. 하지만 알림을 데이터 베이스에 저장하는 상황에서 예외가 발생하다면 어떻게 대처할 것인가에 대해서는 설명하지 않았습니다. 이번 편에서는 어떻게 대처할 수 있을까에대해 알아보겠습니다. 필자가 설명하는 방법에 대해 잘못된 방법이 있을 수 있으니 댓글을 남겨주시면 감사하겠습니다.

 

RabbitMQ Dead Letter Exchange


  • 구글링을 통해 예외를 어떻게 처리할 수 있을까에 대해 살펴보던 중 Dead Letter에 대해 알게되었는데 이것을 사용하면 효과적으로 처리할 수 있지 않을까 싶어서 적용을 하게 되었습니다.
  • Dead Letter Queue를 사용하는 이유는 Consumer에서 예외가 발생하면 기본적으로 예외가 발생한 메시지를 Queue에 다시 넣고 성공할 때까지 무한히 재시도하게 됩니다. 이때 무한히 재시도 하는것을 방지하기 위해 DLX(Dead-Letter-Exchange)로 보내는 것입니다.

RabbitMQ 설정 수정


💡 yml 파일 수정

  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    listener:
      simple:
        retry:
          enabled: true
          initial-interval: 3s  # 최초 재시도 간격 시간
          max-interval: 10s     # 재시도 간격 시간
          max-attempts: 2       # 재시도 최대 횟수
          multiplier: 2         # 재시도 간격 시간을 점진적으로 높입니다.
                                # initial-interval * multiplier 계산되며 max-interval 값보다 높으면 max-interval 값을 사용합니다.

 

💡 RabbitMQ Config 수정

  • reject된 메시지를 Dead Letter Queue로 전송할 수 있도록 기존 Queue를 수정해줍니다.
  • Dead Letter에 대한 Queue, Exchange, Binding을 설정해줍니다.
@EnableRabbit
@Configuration
public class RabbitMQConfig {

    @Bean
    Queue queue() {
        return QueueBuilder.durable("server-queue")
                .deadLetterExchange("deadLetterExchange")
                .build();
    }

    @Bean
    public Queue deadLetterQueue() {
        return QueueBuilder.durable("dead-letter-queue").build();
    }

    @Bean
    FanoutExchange exchange() {
        return new FanoutExchange("fanoutExchange");
    }

    @Bean
    FanoutExchange deadLetterExchange() {
        return new FanoutExchange("deadLetterExchange");
    }

    @Bean
    Binding binding() {
        return BindingBuilder
                .bind(queue())
                .to(exchange());
    }

    @Bean
    Binding deadLetterQueueBinding() {
        return BindingBuilder
                .bind(deadLetterQueue())
                .to(deadLetterExchange());
    }

    @Bean("rabbitTemplate")
    RabbitTemplate rabbitTemplate(@Qualifier("RabbitConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter());
        return rabbitTemplate;
    }

    @Primary
    @Bean("RabbitConnectionFactory")
    @ConfigurationProperties("spring.rabbitmq")
    public CachingConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        return connectionFactory;
    }

    @Bean
    MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

 

💡 RabbitMQ Consumer 수정

  • @RabbitListener 어노테이션을 사용하여 Dead Letter Queue를 경청할 수 있도록 설정해줍니다.
  • ackMode를 사용하여 메시지가 유실되지 않도록 처리합니다.
@Component
@RequiredArgsConstructor
public class RabbitMQConsumer {

    private final RabbitTemplate rabbitTemplate;
    private static final String EXCHANGE_NAME = "amq.topic";
    private final ObjectMapper objectMapper;
    private final NotificationService notificationService;

    @RabbitListener(queues = "server-queue")
    public void consume(NotificationMessage notification) {
        NotificationResponse response = notification.notificationSend(notificationService);
        sendMessage(response);
    }

    @RabbitListener(queues = "dead-letter-queue", ackMode = "MANUAL")
    public void deadLetterConsume(NotificationMessage notification) {
        NotificationResponse response = notification.notificationSend(notificationService);
        sendMessage(response);
    }

    private void sendMessage(NotificationResponse response) {
        try {
            String json = objectMapper.writeValueAsString(response);
            rabbitTemplate.convertAndSend(EXCHANGE_NAME, response.getRoutingKey(), json);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

 

 

전체적인 예제 소스는 깃허브에서 확인할 수 있습니다. https://github.com/kdg0209/rabbitmqExample

 

 

GitHub - kdg0209/rabbitmqExample

Contribute to kdg0209/rabbitmqExample development by creating an account on GitHub.

github.com

 

 

 

 

 

 

728x90
반응형