RabbitMQ 정리

김신영·2025년 7월 30일
post-thumbnail

목차

  1. RabbitMQ
  2. AMQP 프로토콜과 핵심 구성요소
  3. Exchange Type
  4. RabbitMQ의 주요 특징

RabbitMQ

RabbitMQ는 AMQP(Advanced Message Queuing Protocol)를 구현한 오픈소스 메시지 브로커입니다. 서로 다른 시스템이나 서비스 간에 비동기적으로 메시지를 주고받을 수 있게 해주는 중간 매개체 역할을 합니다.

왜 RabbitMQ를 사용해야 할까?

1. 시스템 간 느슨한 결합(Loose Coupling)

  • Producer와 Consumer가 서로를 직접 알 필요가 없음
  • 한쪽 시스템이 다운되어도 다른 쪽은 정상 작동 가능

2. 확장성(Scalability)

  • Consumer를 쉽게 추가하여 처리량 증가 가능
  • 부하 분산이 자동으로 이루어짐

3. 신뢰성(Reliability)

  • 메시지 영속성 지원으로 시스템 장애 시에도 메시지 보존
  • ACK 메커니즘을 통한 메시지 전달 보장

AMQP 프로토콜과 핵심 구성요소

AMQP는 메시지 브로커를 위한 표준 프로토콜로, RabbitMQ가 이를 구현한 대표적인 제품입니다.

핵심 구성요소

1. Producer (생산자)

  • 메시지를 생성하고 Exchange로 전송
  • Exchange를 지정하고 라우팅 키를 함께 전달

2. Exchange (교환소)

  • Producer로부터 메시지를 받아 적절한 Queue로 라우팅
  • Exchange Type에 따라 라우팅 방식이 결정됨
  • 메시지를 저장하지 않고 단순히 라우팅만 수행

3. Queue (큐)

  • 메시지가 실제로 저장되는 공간
  • Consumer가 처리할 때까지 메시지를 보관
  • FIFO(First In First Out) 방식이 기본이나, 우선순위 설정 가능

4. Binding (바인딩)

  • Exchange와 Queue 간의 연결 규칙
  • 라우팅 키나 헤더 정보를 기반으로 매칭
  • 하나의 Queue가 여러 Exchange에 바인딩 가능 (N:M 관계)

5. Consumer (소비자)

  • Queue에서 메시지를 가져와 처리
  • 처리 완료 후 ACK를 전송하여 메시지 제거

Exchange Type

1. Direct Exchange

가장 단순한 형태로, 라우팅 키가 정확히 일치하는 Queue에만 메시지를 전달합니다.

예시:
- 라우팅 키: "payment.completed"
- Queue A 바인딩 키: "payment.completed" ✓ (전달됨)
- Queue B 바인딩 키: "payment.failed" ✗ (전달 안됨)

2. Topic Exchange

와일드카드를 사용하여 패턴 매칭을 수행합니다.

  • * : 정확히 하나의 단어 대체
  • # : 0개 이상의 단어 대체
예시:
- 라우팅 키: "payment.order.completed"
- Queue A 바인딩: "payment.*" ✗ (단어가 2개)
- Queue B 바인딩: "payment.#" ✓ (전달됨)
- Queue C 바인딩: "*.order.*" ✓ (전달됨)

3. Fanout Exchange

바인딩된 모든 Queue에 메시지를 브로드캐스트합니다. 라우팅 키는 무시됩니다.

예시: 시스템 전체 공지사항
- 모든 바인딩된 Queue에 동일한 메시지 전달
- 라우팅 키와 무관하게 작동

4. Headers Exchange

메시지 헤더의 key-value 쌍을 기반으로 라우팅합니다.

  • x-match=all: 모든 헤더가 일치해야 함
  • x-match=any: 하나 이상의 헤더가 일치하면 됨

RabbitMQ의 주요 특징

1. 메시지 분배 (Dispatching)

  • Round-Robin: 여러 Consumer가 있을 때 순차적으로 메시지 분배
  • Fair Dispatch: Prefetch Count를 설정하여 공평한 작업 분배

2. 메시지 신뢰성

  • ACK 메커니즘: Consumer가 처리 완료 신호를 보낸 후에만 메시지 제거
  • Message Durability: 디스크에 메시지를 저장하여 서버 재시작 시에도 보존
  • Publisher Confirms: Producer가 메시지 전달을 확인

3. 순서 보장의 한계

RabbitMQ는 기본적으로 FIFO이지만, 다음 상황에서는 순서가 보장되지 않습니다:

  • 여러 Consumer가 동시에 처리
  • Requeue(재전송) 발생
  • Prefetch 설정이 1보다 큰 경우

RabbitMQ 사용 시 순서를 지키고 싶다면:

큐 하나에 하나의 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 프로토콜을 구현한 강력한 메시지 브로커로, 시스템 간 비동기 통신을 효과적으로 구현할 수 있게 해줍니다.

핵심 정리

  1. Exchange Type 선택

    • Direct: 정확한 라우팅 키 매칭
    • Topic: 패턴 기반 유연한 라우팅
    • Fanout: 모든 큐에 브로드캐스트
    • Headers: 헤더 기반 복잡한 라우팅
  2. 신뢰성 확보

    • Message Durability로 서버 재시작 대비
    • ACK 메커니즘으로 메시지 유실 방지
    • DLQ로 실패 메시지 처리
  1. Best Practices
    • Prefetch=1로 공평한 작업 분배
    • 멱등성 보장으로 중복 처리 방지
    • 적절한 에러 처리와 모니터링

참고

dlq란?

dlq는 다음과 같은 상황에서 정상 큐로 처리되지 못한 메세지를 모아두는 보조 큐이다

dlq로 메세지가 이동하는 대표적인 조건

상황설명
Consumer가 예외를 던지고 ack하지 않음예: AmqpRejectAndDontRequeueException
TTL(Time-To-Live) 초과메시지가 큐에 일정 시간 이상 머무름
큐 용량 초과큐의 최대 메시지 수 초과 시
reject 또는 nackrequeue=false재처리하지 않도록 한 경우

RabbitMQ에서 DLQ 구성 방법

  1. Dead Letter Exchange(DLX) 지정
.withArgument("x-dead-letter-exchange", "momo.dlx")

→ 메시지가 실패하면 momo.dlx 라는 교환기로 보냄

  1. Dead Letter Routing Key 지정
.withArgument("x-dead-letter-routing-key", "momo.dlx.key")

→ 실패 메시지는 "momo.dlx.key" 라우팅 키를 갖고 momo.dlx로 라우팅됨

  1. DLQ 생성 및 바인딩

DLQ 동작 흐름

프로듀서가 momo.exchange → momo.# 키로 메시지 전송

메시지가 momo.queue에 도달

Consumer가 메시지 처리 중 예외 발생 후 ack하지 않음

TTL(10초) 내 미처리 → DLX로 이동

DLX (momo.dlx)가 메시지를 DLQ (momo.dlq)로 라우팅


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은 비싸기 때문에 재활용하고, 가벼운 채널을 분리해서 사용함


사진 출처 : https://www.wallarm.com/what/what-is-amqp

0개의 댓글