발행 확인 메커니즘(트랜잭션, 게시자확인)
basicPublish()가 예외 없이 정상 리턴했는데, 메시지가 브로커에 도달하지 못하는 경우가 있다면?
발행 확인 메커니즘: 트랜잭션과 게시자 확인
왜 발행 확인이 필요한가
소비자 측에서는 ACK/NACK를 통해 메시지 처리 결과를 브로커에 알릴 수 있습니다. 그렇다면 생산자가 발행한 메시지가 브로커에 정상적으로 도달했는지 는 어떻게 확인할 수 있을까요?
발행 확인 메커니즘 이란, 메시지를 브로커에 발행할 때 브로커가 해당 메시지를 정상적으로 수신 및 저장했는지 생산자(Producer)가 확인하는 절차입니다.
예외 처리만으로 충분하지 않은 이유
basicPublish() 호출 시 RabbitMQ 서버에 연결할 수 없으면 ConnectionException 같은 예외가 발생합니다. 그러나 예외가 발생하지 않아도 메시지가 유실되는 상황 이 존재합니다.
| 상황 | 설명 |
|---|---|
| 네트워크 지연 | 연결은 유지되지만 메시지가 브로커에 도달하지 못함 |
| ** 브로커 내부 오류** | 메시지를 수신했지만 디스크 저장 과정에서 실패 |
| ** 브로커 재시작** | 메시지 발행 직후 브로커가 재시작되어 메시지 유실 |
이러한 상황에서는 basicPublish()가 정상적으로 반환되어도 메시지가 실제로 브로커에 저장되지 않을 수 있습니다.
** 예외 처리 **는 네트워크/연결 레벨의 문제를 감지하고, ** 발행 확인 **은 메시지 저장 레벨의 문제를 감지합니다. 두 메커니즘은 ** 상호 보완적 **이며, 안정적인 메시지 발행을 위해서는 둘 다 활용해야 합니다.
두 가지 발행 확인 메커니즘
RabbitMQ는 두 가지 발행 확인 메커니즘을 제공하며, ** 두 모드는 상호 배타적 **입니다. 한 채널에서 하나만 사용할 수 있습니다.
| 비교 항목 | 트랜잭션(Transaction) | 게시자 확인(Publisher Confirms) |
|---|---|---|
| ** 처리 방식** | 동기적 (commit/rollback) | 비동기적 (ack/nack 콜백) |
| ** 성능** | 느림 (매번 브로커 응답 대기) | 빠름 (비동기 응답으로 대량 처리 가능) |
| ** 신뢰성** | 원자성 보장 (여러 메시지 일괄 성공/실패) | 개별 메시지 단위 확인 (원자성 없음) |
| ** 사용 권장** | 원자적 처리가 필수인 특수 상황 | ** 대부분의 실무 환경** |
| ** 성능 영향** | 2~10배 처리량 감소 | 미미한 성능 저하 |
트랜잭션 (Transaction)
트랜잭션이란
트랜잭션은 여러 메시지 발행 작업을 하나로 묶어, ** 모두 성공하거나 모두 실패 **하도록 보장하는 원자적 처리 방식입니다.
- ** 원자성(Atomicity)**: 모든 작업이 성공하거나 모두 실패
- ** 일관성(Consistency)**: 시스템 상태가 일관되게 유지
- ** 격리성(Isolation)**: 동시 실행되는 트랜잭션이 서로 영향을 주지 않음
- ** 지속성(Durability)**: 커밋된 트랜잭션은 영구적으로 저장
트랜잭션 동작 흐름
txSelect() → basicPublish() → txCommit() (성공 시)
→ txRollback() (실패 시)
코드 예시
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
channel.txSelect(); // 트랜잭션 시작
try {
channel.basicPublish("ex", "rk", null, "msg1".getBytes("UTF-8"));
channel.basicPublish("ex", "rk", null, "msg2".getBytes("UTF-8"));
channel.txCommit(); // 모든 메시지 발행 성공 → 커밋
} catch (Exception e) {
channel.txRollback(); // 하나라도 실패 → 전체 롤백
}
}
트랜잭션의 한계
트랜잭션은 매 commit/rollback마다 ** 브로커의 동기적 응답을 기다려야** 하므로, 처리량이 크게 감소합니다. RabbitMQ 공식 문서에서도 대부분의 경우 Publisher Confirms를 권장합니다.
게시자 확인 (Publisher Confirms)
게시자 확인이란
게시자 확인은 메시지를 발행한 후 브로커가 해당 메시지를 정상적으로 처리했는지 ** 비동기로 알려주는 기능 **입니다. 트랜잭션보다 가볍고 빠른 메시지 신뢰성 확보 방법입니다.
동작 원리
- 채널에서 confirm 모드를 활성화합니다.
- 이후 발행되는 모든 메시지에 ** 시퀀스 번호 **가 자동 부여됩니다.
- 브로커가 메시지를 정상 처리하면
ack, 실패하면nack를 응답합니다.
Step 1: 확인 모드 활성화
channel.confirmSelect();
한 번 confirm 모드가 되면 트랜잭션 모드로 전환할 수 없으며, 그 반대도 마찬가지입니다.
Step 2: 비동기 확인 처리 (권장)
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
// deliveryTag까지의 메시지 발행 성공 처리
// multiple=true이면 deliveryTag 이하의 모든 메시지를 일괄 확인
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
// deliveryTag까지의 메시지 발행 실패 처리
// 재발행 로직, 로깅, 알림 등 수행
}
});
// 메시지 발행
channel.basicPublish(exchange, routingKey, props, body);
비동기 방식은 브로커의 응답을 기다리지 않고 ** 계속 메시지를 발행 **할 수 있으므로, 대량 메시지 처리에 최적입니다.
Step 3: 동기 확인 처리 (간단한 경우)
channel.basicPublish(exchange, routingKey, props, body);
try {
channel.waitForConfirmsOrDie(); // 모든 메시지의 ack/nack 응답을 동기적으로 대기
} catch (IOException e) {
// nack 또는 타임아웃 발생 → 메시지 재발행, 로깅 등
}
waitForConfirmsOrDie()는 모든 메시지가 ack되면 정상 종료하고, 하나라도 nack이 발생하거나 응답이 오지 않으면 IOException을 던집니다.
동기 방식은 구현이 간단하지만 대량 처리에는 비동기 방식이 더 효율적입니다.
트랜잭션 vs 게시자 확인 선택 가이드
| 판단 기준 | 트랜잭션 선택 | 게시자 확인 선택 |
|---|---|---|
| 여러 메시지의 원자성이 필수 | O | X |
| 대량 메시지 처리가 필요 | X | O |
| 성능이 중요한 환경 | X | O |
| 구현 복잡도를 낮추고 싶음 | O (동기 흐름) | 비동기 시 다소 복잡 |
메시지 발행 확인이 필요한가?
├─ No → 예외 처리만으로 충분
└─ Yes → 여러 메시지의 원자성이 필요한가?
├─ Yes → 트랜잭션 (txSelect/txCommit)
└─ No → 게시자 확인 (confirmSelect)
├─ 대량 처리 → 비동기 (addConfirmListener)
└─ 소량 처리 → 동기 (waitForConfirmsOrDie)
요약
대부분의 실무 환경에서는 ** 게시자 확인(Publisher Confirms)**을 사용하는 것을 권장합니다. 트랜잭션은 여러 메시지의 원자적 처리가 반드시 필요한 특수한 상황(금융, 결제 배치 등)에서만 선택합니다.
| 메커니즘 | 핵심 특징 |
|---|---|
| ** 트랜잭션** | 원자성 보장, 동기적, 성능 저하 큼 |
| ** 게시자 확인 (비동기)** | 개별 확인, 높은 성능, 대량 처리 적합 |
| ** 게시자 확인 (동기)** | 간단한 구현, 소량 처리 적합 |
** 공식 문서 참고 **: Consumer Acknowledgements and Publisher Confirms