
RabbitMQ는 AMQP(Advanced Message Queuing Protocol)를 구현한 오픈소스 메시지 브로커입니다. 서로 다른 시스템이나 서비스 간에 비동기적으로 메시지를 주고받을 수 있게 해주는 중간 매개체 역할을 합니다.
1. 시스템 간 느슨한 결합(Loose Coupling)
2. 확장성(Scalability)
3. 신뢰성(Reliability)
AMQP는 메시지 브로커를 위한 표준 프로토콜로, RabbitMQ가 이를 구현한 대표적인 제품입니다.

1. Producer (생산자)
2. Exchange (교환소)
3. Queue (큐)
4. Binding (바인딩)
5. Consumer (소비자)
가장 단순한 형태로, 라우팅 키가 정확히 일치하는 Queue에만 메시지를 전달합니다.
예시:
- 라우팅 키: "payment.completed"
- Queue A 바인딩 키: "payment.completed" ✓ (전달됨)
- Queue B 바인딩 키: "payment.failed" ✗ (전달 안됨)
와일드카드를 사용하여 패턴 매칭을 수행합니다.
* : 정확히 하나의 단어 대체# : 0개 이상의 단어 대체예시:
- 라우팅 키: "payment.order.completed"
- Queue A 바인딩: "payment.*" ✗ (단어가 2개)
- Queue B 바인딩: "payment.#" ✓ (전달됨)
- Queue C 바인딩: "*.order.*" ✓ (전달됨)
바인딩된 모든 Queue에 메시지를 브로드캐스트합니다. 라우팅 키는 무시됩니다.
예시: 시스템 전체 공지사항
- 모든 바인딩된 Queue에 동일한 메시지 전달
- 라우팅 키와 무관하게 작동
메시지 헤더의 key-value 쌍을 기반으로 라우팅합니다.
x-match=all: 모든 헤더가 일치해야 함x-match=any: 하나 이상의 헤더가 일치하면 됨RabbitMQ는 기본적으로 FIFO이지만, 다음 상황에서는 순서가 보장되지 않습니다:
큐 하나에 하나의 consumer만 사용
prefetch = 1 (메세지를 한 번에 하나씩만 전달)
autoAck = false + 순차적 ack (처리가 끝나야 ack, 실패 시에는 requeue 하지 않기)
consumer 내부 로직도 동기화
내부적으로 병렬 실행되면 순서는 지킬 수 없음.
등의 방법을 사용할 수 있습니다.
// 예시 설정 (Spring AMQP)
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setPrefetchCount(1);
container.setConcurrentConsumers(1);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
Producer → [Queue: order.status.queue] → Consumer (1명만) → DB 처리 순차적 수행
prefetch =1, autoAck = false
실패 시 dlq로 이동
멱등성 처리 구조
순서가 깨져도 정합성이 깨지지 않도록 처리
중복 메세지를 처리해도 db 상태가 일관되도록 보장
if (alreadyProcessed(event.getId())) return; // 멱등성 체크
process(event);
markAsProcessed(event.getId());
RabbitMQ는 AMQP 프로토콜을 구현한 강력한 메시지 브로커로, 시스템 간 비동기 통신을 효과적으로 구현할 수 있게 해줍니다.
Exchange Type 선택
신뢰성 확보
dlq는 다음과 같은 상황에서 정상 큐로 처리되지 못한 메세지를 모아두는 보조 큐이다
dlq로 메세지가 이동하는 대표적인 조건
| 상황 | 설명 |
|---|---|
Consumer가 예외를 던지고 ack하지 않음 | 예: AmqpRejectAndDontRequeueException |
| TTL(Time-To-Live) 초과 | 메시지가 큐에 일정 시간 이상 머무름 |
| 큐 용량 초과 | 큐의 최대 메시지 수 초과 시 |
reject 또는 nack 후 requeue=false | 재처리하지 않도록 한 경우 |
.withArgument("x-dead-letter-exchange", "momo.dlx")
→ 메시지가 실패하면 momo.dlx 라는 교환기로 보냄
.withArgument("x-dead-letter-routing-key", "momo.dlx.key")
→ 실패 메시지는 "momo.dlx.key" 라우팅 키를 갖고 momo.dlx로 라우팅됨
DLQ 동작 흐름
프로듀서가 momo.exchange → momo.# 키로 메시지 전송
메시지가 momo.queue에 도달
Consumer가 메시지 처리 중 예외 발생 후 ack하지 않음
TTL(10초) 내 미처리 → DLX로 이동
DLX (momo.dlx)가 메시지를 DLQ (momo.dlq)로 라우팅
문제 분석용: 로그를 통해 실패 원인 파악
수동 재처리: 운영자가 메시지를 재전송
자동 재시도 로직: DLQ에서 일정 간격으로 재처리 시도 (재처리 서비스 구성)
RabbitMQ에서는 Connection 하나당 여러 Channel을 열수 있다
producer와 consumer는 각각 별도의 채널을 사용
producer(RabbitTemplate)
메세지를 publish 하기 위해
Consumer(@RabbitListener)
큐에서 메세지를 consume하기 위해
둘 다 ConnectionFactory를 통해 같은 TCP Connection을 공유하지만, 별도의 Channel로 통신
Connection vs Channel
Connection TCP 단위의 연결(보통 하나의 JVM 인스턴스에서 하나만 생성)
Channel Connection 위에서 논리적 스트림. 하나의 Connection에서 수백 개의 Channel 가능
TCP connection은 비싸기 때문에 재활용하고, 가벼운 채널을 분리해서 사용함