-
RabbitMQ에서 At Least Once 보장카테고리 없음 2024. 7. 7. 20:45
메시징 큐 시스템에서 메시지의 신뢰성과 전달 보장은 매우 중요하다. 특히, "at least once" 보장은 메시지가 최소한 한 번은 수신자에게 전달된다는 것을 의미한다. 이번 포스트에서는 Spring Boot를 사용하여 RabbitMQ에서 "at least once" 보장을 구현하는 방법과 RabbitMQ의 2PC(Two-Phase Commit) 지원 방안에 대해 알아보자.
RabbitMQ에서 At Least Once 보장하기
기본적으로 RabbitMQ는 "at least once" 보장을 하지 않는다. 네트워크 장애나 메시지를 보내는 측의 문제로 인해 메시지가 유실될 수 있다. 이를 방지하기 위해 메시지 지속성과 확인 메시지를 활용한 설정이 필요하다.
- 메시지 지속성(Persistence)
- 메시지를 퍼블리싱할 때 지속성 모드를 설정하여 RabbitMQ 서버가 재시작되더라도 메시지가 유실되지 않도록 한다.
- 확인 메시지(Acknowledgements)
- 메시지를 소비한 후 RabbitMQ에 확인 응답(ACK)을 보내야 한다. 이를 통해 메시지를 처리한 후에만 큐에서 제거할 수 있다.
- 메시지 재전송
- 메시지 처리 중 오류가 발생하면 RabbitMQ가 해당 메시지를 다시 큐에 넣고 재전송한다.
아래는 이러한 설정을 반영한 퍼블리셔와 컨슈머 코드이다.
퍼블리셔 코드
퍼블리셔 코드에는 지속성 큐를 설정하여 메시지를 전송한다.
@Bean public Queue myQueue() { return new Queue("myQueue", true); // 지속성 큐 설정 }
컨슈머 코드
컨슈머 코드에는 메시지를 수신하고 처리한 후 수동으로 ACK 또는 NACK를 보내는 로직을 추가한다.
@Bean public Queue myQueue() { return new Queue("myQueue", true); // 지속성 큐 설정 } @RabbitListener(queues = "myQueue") public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) { try { // 메시지 처리 로직 channel.basicAck(tag, false); // 메시지 처리 성공 시 ACK 전송 } catch (Exception e) { channel.basicNack(tag, false, true); // 메시지 처리 실패 시 NACK 전송 } }
이제 퍼블리셔와 컨슈머 서비스가 메시지를 지속성 큐에 전송하고, 수신한 메시지를 수동으로 확인(ACK) 또는 거부(NACK)하여 "at least once" 보장을 구현가능하게 만들었다.
수동 확인(ACK/NACK) 및 메시지 지속성 설정은 성능에 영향을 줄 수 있다.
- 메시지 지속성:
- 메시지를 지속성 큐에 저장하면 RabbitMQ는 메시지를 디스크에 기록하므로 디스크 I/O가 추가된다. 이는 메시지 전송 속도를 낮출 수 있다.
- 수동 확인(ACK/NACK):
- 수동 확인을 사용하면 메시지를 처리한 후 ACK 또는 NACK를 보내야 한다. 이 과정은 자동 확인보다 시간이 더 걸릴 수 있다.
- 특히, 메시지 처리 로직이 복잡하거나 시간이 많이 걸리는 경우 전체 처리 시간이 길어질 수 있다.
이러한 성능 저하를 최소화하기 위한 몇 가지 방법도 있다.
- Batch ACK: 여러 메시지를 한 번에 ACK하여 성능을 향상시킬 수 있다. 이는 네트워크 통신을 줄여준다.
- QoS 설정: prefetch 설정을 사용하여 소비자가 한 번에 처리할 수 있는 메시지 수를 제한할 수 있다. 이를 통해 메시지 처리량을 조절하고 과부하를 방지할 수 있음.
- 비동기 처리: 메시지 처리를 비동기로 처리하여 병렬로 여러 메시지를 동시에 처리할 수 있음.
메시지가 컨슈머에게 도달하지 않았을 경우, 컨슈머는 NACK를 보낼 수 없다. 이러한 경우에 "at least once" 보장을 제공하기 위해서는 추가적인 방안이 필요하다.
"At Least Once" 보장을 위한 추가적인 메커니즘
- 메시지 지속성(Persistence): 메시지가 RabbitMQ 서버에 안전하게 저장되도록 보장한다. 이렇게 하면 서버 재시작이나 네트워크 문제에도 메시지가 손실되지 않는다.
메시지를 퍼블리싱할 때, deliveryMode를 persistent로 설정 및 큐를 생성할 때 durable 속성을 true로 설정하여 디스크에 저장되도록 한다.
2. 퍼블리셔 확인(Publisher Confirms): 메시지를 브로커에 보낸 후, 브로커가 메시지를 안전하게 수신했음을 퍼블리셔에게 확인해주는 방식. 퍼블리셔는 확인 응답을 기다리며, 응답이 오지 않으면 메시지를 다시 전송할 수 있다.
channel.confirmSelect(); if (channel.waitForConfirms()) { // 메시지가 브로커에 성공적으로 도달함 } else { // 메시지가 브로커에 도달하지 않음, 재전송 로직 필요 }
3. 컨슈머 연결 상태 모니터링: RabbitMQ는 소비자와의 연결 상태를 모니터링하여 연결이 끊어진 경우 이를 감지할 수 있다. 메시지가 특정 소비자에게 전달되지 않으면 RabbitMQ는 메시지를 다시 큐에 넣어 다른 소비자가 처리할 수 있도록 한다.
출처
- http://www.rabbitmq.com/tutorials/amqp-concepts.html
- [AMQP 0-9-1 Model Explained | RabbitMQ
- 메시지 지속성(Persistence)