JAVA/SpringBoot
Spring Boot - RabbitMQ를 사용하여 알림 발송 2편
realizers
2022. 11. 20. 17:01
728x90
반응형
지난 이야기
- 지난 이야기에서는 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
728x90
반응형