Search

RabbitMQ 이해하기

Written by
음하
음하
Date published
2024/06/10
Series
4 more properties
Introduction
회사에서 특정 이벤트에 따라 데이터를 집계하는 시스템을 개발하기 위해서 별도의 집계 프로그램을 개발하고 중간에 메시지 브로커를 두는 설계를 진행하게 되었다. 이 때 많은 자료를 조사하게 되었는데 그 중 추려 본 것이 RabbitMQ와 Kafka 였다. 현재 회사 구조에서 Kafka를 사용하기 위해서 들어가는 비용과 Kafka 만 심도 있게 관리할 수 있는 인력이 없다는 판단하에 RabbitMQ를 사용하고 후에 Kafka로 전환하는 전략을 취하는 전략을 선택했다. 이번 시간에는 RabbitMQ를 도입하면서 공부한 내용을 작성해 보았다.

1. RabbitMQ

RabbitMQAMQP를 구현하여 메시지 생성자와 소비자 사이에서 메시지를 중계해 주는 ‘메시지 브로커’이다.
AMQP(Advenced Message Queuing Protocol) : 메시지 지향 미들웨어를 위한 개발형 표준 응용 계층 프로토콜
RabbitMQ는 메시지 지향 미들웨어(Message Oriented Middleware)에 속해있다. 메시지 지향 미들웨어는 비동기 메시지를 사용하는 응용 프로그램들 사이에서 데이터를 송수신하는 것을 의미하며, 이러한 시스템을 구현한 솔루션을 ‘메시지 큐(Message Queue)라고 한다.

RabbitMQ 사용 시 이점

1.
비동기(Asynchronous)
메시지 큐의 경우 생산된 메시지의 저장, 전송에 대해 동기화 처리를 하지 않고 Queue에 넣어두기 때문에 나중에 처리할 수 있으며, 이러한 방식으로 인해 기존의 동기화 방식에서 발생할 수 있는 병목 현상을 방지할 수 있다.
2.
낮은 결합도(Decoupling)
메시지를 생산하여 발송하는 서비스(Producer)와 메시지를 받아서 처리하는 서비스(Consumer)가 독립적으로 행동하면서 서비스 간의 결합도가 낮아지는 장점이 있다.
3.
확장성(Scalable)
다수의 프로세스들이 메시지 큐를 통해 메시지를 보낼 수 있기 때문에 확장성 및 분산 처리에 대한 장점이 있다.
4.
탄력성(Resilience)
메시지를 받아서 처리하는 서비스가 다운(중지) 되더라도 메시지 큐가 중단되는 것은 아니기 때문에 메시지는 Queue에 남아있게 되며, 중지된 서비스가 재시작되면 Queue에 있는 메시지를 다시 처리할 수 있다.
5.
보장성(Guarantees)
메시지 큐는 보관되는 모든 메시지가 결국 Consumer 서비스에게 전달된다는 일반적인 보장을 제공한다.

2. RabbitMQ 구조

Producer
요청을 보내는 중체, 보내고자 하는 메시지를 Exchange에 publish 한다.
Consumer
Producer로부터 메시지를 받아 처리하는 주체이다.
Exchange
Producer로부터 전달받은 메시지를 어떤 Queue로 발송할지 결정하는 객체로 4가지 타입(Direct, Topic, Headers, Fanout)이 있으며, 각각의 타입과 Binding 규칙에 따라 적절한 Queue로 메시지를 전달한다.
Queue
Consumer가 메시지를 consume하기 전까지 보관하는 장소
Binding
Exchange와 Queue를 연결하는 관계이다. 보통 사용자가 특정 Exchange가 특정 Queue를 바인딩하도록 정의한다. (Fanout은 제외)

Exchange 속성

Name: Exchange 이름
Type: 메시지 전달 방식
Direct, Fanout, Topic, Headers
Durability: 브로커가 재시작될 때 남아있는지 여부
Durable: 브로커가 재시작되어도 디스크에 저장되어 남아있음
Transient: 브로커가 재시작되면 사라짐
Auto delete: 마지막 Queue 연결이 해제되면 삭제

Queue 속성

Name: Queue 이름 (amq. 은 예약어로써 사용 불가)
Durability: 브로커가 재시작될 때 남아있는지 여부
Durable: 브로커가 재시작되어도 디스크에 저장되어 남아있음
Transient: 브로커가 재시작되면 사라짐
Auto delete: 마지막 Consumer가 consume을 끝낼 경우 자동 삭제
Argument: 메시지 TTL, Max Length 같은 추가 옵션 명시

3. Exchange 4 Types

Type
Description
Direct
라우팅 키가 정확히 일치하는 Queue에 메시지 전송
Topic
라우팅 키 패턴이 일치하는 Queue에 메시지 전송
Headers
[key:value]로 이루어진 header값을 기준으로 일치하는 Queue에 메시지 전송
Fanout
해당 Exchange에 등록된 모든 Queue에 메시지 전송

Direct Exchange

메시지를 전달할 때 메시지에 포함된 라우팅 키(routing key)를 이용하여 정확히 일치하는 Queue에만 전송한다.
하나의 Queue에 여러 라우팅 키를 지정할 수 있고, 여러 Queue에 같은 라우팅 키를 지정할 수도 있다.
Default Exchange : 이름이 없는 Direct Exchange이며, RabbitMQ에서 생성되는 Queue가 자동으로 바인딩되는데 이때 각 Queue의 이름이 라우팅 키로 지정된다.

Topic Exchange

라우팅 키의 패턴을 이용해 메시지를 라우팅하는 방식이다.
여러 Consumer에서 메시지 형태에 따라 선택적으로 수신해야하는 경우 등등 다양한 패턴 구현에 활용될 수 있다.
여기서 사용되는 binding key는 점(".")으로 구분되는 단어의 조합으로 정의된다. ("*") 또는 ("#")을 사용할 수 있으며, ("*")은 정확히 하나의 단어를 대체하고 ("#")은 0개 혹은 여러 개의 단어를 대체한다.
위 그림의 경우에는 라우팅 키가 정확히 일치하지 않아 Queue2는 바인딩되지 않은 경우이다.
위 그림의 경우에는 "animal.rabbit""animal.*""#" 모두에 일치하기 때문에 모든 Queue에 전송되는 경우이다.

Headers Exchange

Topic과 유사한 방법이지만 라우팅을 위해 header를 사용한다는 점에서 차이가 있다.
Producer에서 정의된 header의 key-value 쌍과 consumer에서 정의된 Argument의 key-value 쌍이 일치하면 binding된다.
binding key 만을 사용하는 것보다 더 다양한 속성을 사용할 수 있다.
이 타입을 사용하면 routing key는 무시되고, 바인딩 시 지정된 값과 헤더 값이 같은 경우에만 일치하는 것으로 간주된다.
Header Exchange는 x-match 값에 따라 아래와 같이 동작 방식에 차이가 있다.
key
value
description
x-match
all
header의 모든 key-value 쌍 값과 argument의 모든 key-value쌍 값이 일치할 때 binding
x-match
any
argument의 key-value 쌍 값 중 하나라도 header의 key-value 쌍 값과 일치할 때 binding

Fanout Exchange

라우팅 키에 관계없이 Exchange에 등록된 모든 Queue에 동일한 메시지를 전달하는 브로드캐스트 라우팅 방식으로 동작한다. 즉, Exchange에 등록된 모든 Queue에 메시지를 전송하는 방식이다.
브로드캐스팅(broadcasting) : 송신 호스트가 전송한 데이터가 모든 호스트에게 전송되는 방식을 의미한다.

4. Round-Robin Dispatch, Fair Dispatch

만약 하나의 Queue에 여러 개의 Consumer가 붙어있다면 RabbitMQ에서는 Round-Robin 방식을 통해 Consumer에게 메시지를 균등하게 분배(Fair Dispatch)한다. 이러한 방식을 통해 메시지를 병렬적으로 처리할 수 있다.

Prefetch Count

하지만 Round-Robin 방식이 무조건 효율적일 거라고는 볼 수 없다.
예를 들어 하나의 Queue에 두 개의 Consumer가 존재하고, 이 두 개의 Consumer에는 Round-Robin 방식으로 메시지가 분배된다. 이때, 만약 홀수 번째의 메시지는 처리 시간이 매우 길고 짝수 번째 메시지는 처리 시간이 짧은 경우, 홀수 번째 메시지를 처리하는 Consumer에는 처리해야 할 메시지가 계속 누적되는 경우가 발생할 수 있다.
이러한 상황을 예방하기 위해서 RabbitMQ에서는 'Prefetch'를 설정할 수 있다.
Prefetch는 Queue의 메시지를 Consumer의 메모리에 쌓아둘 수 있는 최대 메시지의 양으로 Prefetch Count의 값을 1로 설정하면 하나의 메시지가 처리되기 전에는 새로운 메시지를 받지 않기 때문에 작업을 분산시킬 수 있다.
Search