RabbitMQ 아키텍처

 

1. 들어가며

 

Message Queue 하면 가장 먼저 떠오르는 두 개는 Kafka와 RabbitMQ 아닌가 합니다.

그래서 오늘은 Kafka에 이어 RabbitMQ에 대해 정리해보고자 합니다.똑같은 메시지 큐가 뭐가 다른가 싶겠지만 둘은 구현된 방식에 큰 차이가 있습니다. 또한 그 구현 방식에 따라 특화된 분야도 매우 다릅니다. 둘의 차이점은 다음 글으로 남겨두고 오늘은 RabbitMQ가 어떤 아키텍처로 구성 되어있는지 살펴보도록 하겠습니다.

 

 

2. RabbitMQ 아키텍처

RabbitMQ란?
메시지 생성자와 소비자 사이에서 메시지를 중계해주는 '메시지 브로커'입니다. 
비동기 메시지를 사용하는 프로그램들 사이에서 데이터를 송수신하며 교환소(Exchange)와 Queue의 구조로 이루어져 있습니다.

 

- RabbitMQ의 장점

 

RabbitMQ를 왜 프로그램 사이 중계자로 사용해야 할까요? 다음과 같은 메리트가 있습니다.

 

1. 비동기(Asynchronous)

메시지 큐의 경우 생산된 메시지의 저장, 전송에 대해 동기화 처리를 하지 않고 Queue에 넣어두기 때문에 나중에 처리할 수 있으며, 이러한 방식으로 인해 기존의 동기화 방식에서 발생할 수 있는 병목 현상을 방지할 수 있습니다.

 

2. 낮은 결합도(Decoupling)

메시지를 생산하여 발송하는 서비스(Producer)와 메시지를 받아서 처리하는 서비스(Consumer)가 독립적으로 행동하면서 서비스 간의 결합도가 낮아지는 장점이 있습니다. 예를 들어 각 서비스가 API 콜로 이루어져 있다고 하면, 다른 서버의 장애가 전파될 확률이 높습니다. 해당 API 콜이 지연되면서 스레드를 계속 점유하게 되고, CPU 자원이 낭비되기 때문입니다. 물론 Message Queue에서도 마찬가지로 브로커 API 장애가 있을 수 있기 때문에 서킷 브레이커 패턴을 활용할 필요는 있습니다.

 

3. 확장성(Scalable)

다수의 프로세스들이 메시지 큐를 통해 메시지를 보낼 수 있기 때문에 확장성 및 분산 처리에 대한 장점이 있습니다.

 

4. 탄력성(Resilience)

메시지를 받아서 처리하는 서비스가 다운(중지) 되더라도 메시지 큐가 중단되는 것은 아니기 때문에 메시지는 Queue에 남아있게 되며, 중지된 서비스가 재시작되면 Queue에 있는 메시지를 다시 처리할 수 있습니다.

 

5. 보장성(Guarantees)

메시지 큐는 보관되는 모든 메시지가 결국 Consumer 서비스에게 전달된다는 일반적인 보장을 제공합니다.

 

- RabbitMQ 구조

RabbitMQ는 'Producer', 'Consumer', 'Exchange', 'Binding', 'Queue'로 이루어져 있습니다.

 

 

 

RabbitMQ 아키텍처

 

- Producer

메시지를 생성하고 발송합니다. Producer는 메시지를 Queue에 직접 보내는 것이 아니라 Exchage에 Publish 한다는 특징이 있습니다. Kafka에서 비동기로 전송한 방식을 떠올려 보면 됩니다.

 

- Exchange

  • Producer로부터 전달받은 메시지를 어떤 Queue에 발송할지 결정합니다.
  • 4가지 타입(Direct, Topic, Headers, Fanout)으로 나뉘어 있습니다.
    • Direct: 메시지에 포함된 Routing Key를 기반으로 Queue에 메시지를 전달합니다.
    • Topic: 메시지에 포함된 Routing Key의 패턴을 이용하여 메시지를 전달합니다. (패턴: (*) 또는 (#)을 사용할 수 있으며, (*)는 정확히 하나의 단어를 대체하고 (#)은 0개 혹은 여러 개의 단어를 대체합니다.)
    • Headers: Routing Key 속성은 무시되고 header 속성의 값이 바인딩 시 지정된 값과 같은 경우 일치하는 것으로 간주하여 전달합니다.
    • Fanout: 모든 Queue에 메시지를 전달합니다.
  • 각각의 타입과 Binding 규칙에 따라 적절한 Queue로 메시지를 전달합니다.

 

- Binding

  • Exchange와 Queue를 연결하는 관계입니다.
  • Exchante Type과 Binding 규칙에 따라 적절한 Queue로 메시지가 전달됩니다.

 

- Queue

  • RabbitMQ 안에서 메시지를 일시적으로 저장하는 장소입니다.
  • Consumer들은 Queue에 저장된 메시지를 읽어 들입니다.
  • Queue는 RabbitMQ 서버가 설치되는 호스트의 디스크 용량 및 메모리에 한정된다는 특징이 있습니다.
  • Queue는 다음과 같이 정의할 수 있습니다.
    • Name: queue 이름. amq. 로 시작되는 이름은 예약되어 사용할 수 없습니다.
    • Durability: 브로커 재시작 시 디스크에 저장되어 있을지 아닐지 규정합니다. 다만 메시지는 해당 내구성 대상이 아닙니다.
    • Auto delete: 마지막 Consumer가 구독을 끝내는 경우 Queue를 삭제합니다.
    • Arguments: 메시지 TTL, Max Length와 같은 추가 기능을 명시합니다.

- Consumer

메시지를 받아서 처리합니다. 하나의 Queue에 여러 Consumer가 붙어있다면 RabbitMQ에서는 Round-Robin 방식을 통해 Consumer에게 균등하게 분배합니다.

 

다만 Round-Robin 특성상 불균형이 발생할 수 있습니다. 예를 들어 두 개의 Consumer가 있고 Round-Robin 방식으로 분배받는다고 해보겠습니다. 그렇다면 만약 홀수 번째의 메시지는 처리 시간이 매우 길고 짝수 번째 메시지는 처리 시간이 짧은 경우, 홀수 번째 메시지를 처리하는 Consumer에는 처리해야 할 메시지가 계속 누적되는 경우가 발생할 수 있는데요.

 

이러한 상황을 예방하기 위해서 RabbitMQ에서는 'Prefetch'를 설정할 수 있습니다.

Prefetch는 Queue의 메시지를 Consumer의 메모리에 쌓아둘 수 있는 최대 메시지의 양

 

Prefetch Count의 값을 1로 설정하면 하나의 메시지가 처리되기 전에는 새로운 메시지를 받지 않기 때문에 작업을 분산시킬 수 있습니다.

 

- AMQP 수신 확인 모델

AMQP는 네트워크에 문제가 발생하거나 요청을 처리하지 못했을 경우, 즉 장애가 발생했을 때를 대비하여 2가지 수신 확인 모델을 가집니다.

  • Consumer가 메시지를 받으면 브로커에게 통지하고, 브로커는 통지를 받았을 때만 Queue에서 해당 메시지를 삭제합니다. 
  • 브로커가 메시지를 전달하면 자동으로 삭제합니다.