교재 학습 - 메시지 큐, Rabbit MQ에 대하여 (번외)

Kyu0·2023년 4월 3일
0

0. 도입

스프링 부트를 활용한 마이크로 서비스 개발 교재를 학습하면서 메시지 큐에 대해서 다뤄봤습니다. 교재에서는 간략하게 설명하고 끝내기 때문에 더 깊은 지식을 얻고 소개하기 위해 해당 게시글을 작성했습니다.

이번 게시글에서 다룰 주제는 메시지 큐란 무엇인가?, MSA와 메시지 큐의 관계, RabbitMQ 설정 및 실습입니다.


1. 메시지 큐란 무엇인가?

메시지 큐(Message Queue) 에 대해서 알아보기 이전에 메시지와 큐를 분리하여 각각의 단어가 무슨 의미인지 알아보도록 하겠습니다.

1-1. 메시지란 무엇인가?

여러분들도 아시다시피 메시지(Message)의 의미는 우리가 일상에서 사용하는 스마트폰의 문자 기능과 같습니다. 하지만 메시지 큐에 대해서 더 디테일 알기 위해서는 문자를 보내는 과정을 한 번 자세히 살펴보면 좋을 것 같습니다.

위와 같이 문자를 보내는 과정은 문자 작성, 문자 보내기, 문자 수신 으로 나누어져 있습니다. 저는 여기서 문자를 보낸 이후, 발신자는 수신자가 해당 문자를 확인했는지 안했는지 여부에 대한 사실보다는 내가 문자를 보냈다는 사실이 중요하다고 생각합니다.

반대로, 수신자에게는 발신자가 문자를 보냈다는 사실이 아니라 내가 문자를 받았다는 사실이 중요합니다. 위의 그림을 발신자/수신자가 관심있는 영역을 나누어보면 다음과 같은 그림이 그려집니다.

메시지 큐에서도 생성자(Publisher와) 소비자(Consumer)가 나뉘어 생성자(발신자)는 이벤트(문자)를 생성하여 발신하고, 소비자(수신자)는 이벤트(문자)를 수신하는데에 집중합니다.

1-2. 큐란 무엇인가?

출처 : freepik

큐(Queue)란 간단히 말해 대기열입니다. 우리가 수강 신청, 티켓팅 등의 서비스에 여러 이용자가 몰리면 1,000명이 대기 중입니다. (예상 소요시간 10분) 이라는 메시지를 본 적이 있을 겁니다. 이와 같이 서비스의 처리 속도가 이용자의 요청 속도를 따라갈 수 없을 때 앞선 요청들을 처리하는 동안 대기하는 요청들을 잠시 큐에 담아두고 해당 요청들을 처리할 수 있는 컴퓨터 자원이 확보되는 순간 요청들을 하나씩 꺼내어 처리하는 경우입니다.

출처 : 충남대학교 예술대학

정리하자면, 큐는 개체들간의 서로 다른 처리 속도를 극복하기 위한 자료구조라는 것을 알 수 있습니다. 그럼 다시 돌아가서, 메시지 큐란 무엇일까요?

1-3. 메시지 큐란 무엇인가?

앞서 소개한 '메시지'와 '큐'의 정의를 혼합하면 이렇게 말할 수 있을 것 같습니다.

생산자의 요청 정보를 바로 소비자에게 전달하는 것이 아닌, 큐에 저장해뒀다가 소비자가 처리할 수 있을 때 꺼내어 주는 시스템이며, 이를 통해 생산자/소비자 간에 처리 속도 차이를 극복할 수 있으며 각각 관심있는 분야(발신, 수신)에 대해서만 다루기 때문에 서비스 간 결합도가 낮아지고 작업을 효율적으로 할 수 있다.

여기서 다음과 같이 중요한 키워드를 뽑을 수 있습니다.

  • 비동기 (각각 관심있는 분야에 대해서만 다룬다)
    메시지 큐에서 생산자는 생성한 메시지에 대해서 발송에만 관심이 있고 소비자가 해당 메시지를 어떻게 소비하는 지에 대해서는 관심이 없습니다. 메시지만 휙 던져주고 다시 자기의 할 일을 하러 떠날 뿐입니다.

  • 낮은 결합도, 탄력성(서비스 간 결합도가 낮아진다)
    메시지 큐는 생산자와 소비자, 그 중간에 위치하는 미들웨어이기 때문에 어느 한 쪽에 변경사항이 있을 경우 메시지 큐가 완충 작용을 해 다른 쪽에는 큰 영향이 없도록 합니다. 또한 생산자와 소비자가 서로 종속되어 있지 않기 때문에 어느 한 쪽에 오류가 발생해도 메시지 큐를 통해 메시지를 저장/소비할 수 있습니다.

이 외에도 확장성, 보장성등의 특징을 가지고 있습니다.

2. MSA와 메시지 큐의 관계는?

그럼 이제 MSA(MicroService Architecture)와 메시지 큐의 관계에 대해서 알아보겠습니다. 우선 MSA는 한 프로그램의 기능들을 나누어 작은 서비스(마이크로서비스)로 분리하고 나누어진 서비스들간에 상호 작용으로 요청을 수행하는 소프트웨어 구조입니다.

여기서 마이크로서비스들은 각각 독립적으로 실행되기 때문에 서로 간의 통신을 비동기적으로 해야할 때가 많습니다. 이를 해결해주는 것이 바로 메시지 큐입니다. 마이크로서비스가 데이터를 주고 받을 때 메시지 큐를 거침으로써 마이크로서비스의 독립성과 비동기성을 확보할 수 있는 것입니다.

따라서, MSA에서의 메시지 큐는 마이크로서비스들 간에 통신을 하기 위해 거치는 통로라고 할 수 있습니다.

출처 : 컴퓨터월드, MSA와 애자일(Agile)

3. RabbitMQ에 대해서

RabbitMQ는 메시지 큐의 표준 프로토콜 중 하나인 AMQP(Advanced Message Queuing Protocol)를 구현한 오픈 소스 메시지 브로커 소프트웨어입니다.

다음의 그림은 RabbitMQ(정확히는 AMQP 0.9.1)의 시스템 구조입니다.

출처 : https://www.rabbitmq.com/tutorials/amqp-concepts.html

Publisher(생산자), Queue(큐), Consumer(소비자)에 대해서는 앞선 항목들에서 알아봤으니 새로운 개념인 Exchange에 대해서 알아보겠습니다.

3-1. Exchange

Exchange는 생산자가 전달한 메시지를 큐에 담아주는 역할을 합니다. 여기서 메시지를 전달받는 큐는 배포 규칙에 따라 1개가 될 수도, 여러 개가 될 수도 있습니다. 이러한 배포 규칙은 4가지로, 각각 Direct Exchange, Fanout Exchange, Topic Exchange, Headers Exchange가 있습니다.

3-1.1. Direct Exchange

출처 : https://www.rabbitmq.com/tutorials/amqp-concepts.html Direct Exchange 방식은 라우팅 키를 이용해 바인딩 키와 일치하는 큐에 대해 메시지를 전송합니다.(라우팅 키 == 바인딩 키) RabbitMQ 공식 문서에서는 유니캐스트 라우팅에 적절한 방식이지만 멀티캐스트 라우팅에도 사용할 수 있다고 합니다.

이 때, 라우팅 키는 메시지와 함께 사용되며 어떤 큐에 메시지를 전달할지 결정하는 역할을 합니다. 바인딩 키는 큐와 함께 사용되며 교환기에 연결하는 데에 사용됩니다.

간단한 예시로, 생산자 A가 {'routingKey': 'A', 'message': 'hello'} 라는 메시지를 Exchange에게 전달하면, Exchange는 자신과 연결된 큐들 중 바인딩 키가 A인 큐에게 해당 메시지를 전달하게 됩니다.

RabbitMQ에서는 두 용어를 혼용하고 있기 때문에 헷갈린다는 의견도 있습니다. 따라서, 공식 문서를 보실 때에는 문맥에 따라 구분하여 읽는 것이 좋아보입니다.

3-1.2. Fanout Exchange

출처 : https://www.rabbitmq.com/tutorials/amqp-concepts.html

Exchange와 연결된 모든 큐들에 대해 메시지를 전송하는 방식입니다. 메시지에 라우팅 키가 담겨져서 전송되는 경우에 해당 라우팅 키를 무시하고 메시지를 모든 큐들에게 전송합니다.

3-1.3 Topic Exchange

출처 : https://www.javainuse.com/messaging/rabbitmq/exchange

Topic Exchange는 Direct Exchange와 유사하지만 라우팅 키와 바인딩 키를 비교할 때 패턴 간의 일치 여부를 확인합니다. 위의 그림에서는 바인딩 키가 각각 queue.admin, queue.marketing, queue.finance, queue.*인 큐가 있고, 라우팅 키가 queue.admin인 메시지가 전송되는 상황입니다.
라우팅 키와 바인딩 키가 일치하는 adminQueue, 패턴이 일치하는 allQueue에 대해서 해당 메시지가 전송됩니다. 정규표현식 패턴 매칭과 비슷하죠?

3-1.4 Headers Exchange

출처 : https://www.oreilly.com/library/view/practical-real-time-data/9781787281202/b8086831-40a8-4595-9fb2-24ff6de7d42f.xhtml

Headers Exchange 방식은 라우팅 키가 아닌 메시지 헤더의 key-value가 큐에 바인딩된 헤더 key-value와 일치하면 메시지를 전송하는 방식입니다.

그림에서는 헤더에 {"key1", "value1"} 이라는 key-value 쌍이 들어있고, 큐에 바인딩 된 헤더 key-value 쌍은 각각 {"x-match": "any", "key1": "value1", "key2", "value2"}, {"x-match": "any", "key3": "value4"}, {"x-match": "all", "key1": "value1", "key2", "value2"}인 큐가 있습니다.

여기서 x-matchany, all의 값을 가지며, 각각 key-value 쌍이 하나라도 일치한다면 전송, 모두 일치한다면 전송한다는 의미를 가지고 있습니다.


4. RabbitMQ 설치

macOS에서 설치는 HomeBrew를 이용해서 설치합니다. 터미널에서 아래의 명령어를 순차적으로 입력해주세요.

brew update
brew install rabbitmq

설치가 완료되면 다음과 같은 경로에 파일이 생성됩니다.

Intel MacsApple Silicon Macs
서버 스크립트 / CLI 도구/usr/local/opt/rabbitmq/sbin/opt/homebrew/opt/rabbitmq/sbin
바이너리 파일/usr/local/sbin/opt/homebrew/sbin

저는 바이너리 파일을 통해 RabbitMQ 서버를 실행하기 위해 터미널에서 /usr/local/sbin/rabbitmq-server 명령어를 입력했습니다. (Intel Mac 사용 중)

그 후 localhost:15672 로 접속한 후 Username: guest, Password: guest를 입력해 로그인합니다.

이렇게 현재 RabbitMQ에 대한 상태를 볼 수 있습니다.


5. RabbitMQ 실습

완성된 예제 프로젝트는 깃허브에 남겨뒀습니다. (링크)

서비스 구성은 MQ-PUB(생산자), MQ-CON(소비자)로 이루어져 있습니다. 각 프로젝트의 application.properties 파일에 RabbitMQ에 관련한 설정과 환경 변수 설정값을 정의해두었습니다.

프로젝트의 간략한 동작은 다음과 같습니다.

  1. MQ-PUB 서비스가 3초 마다 '테스트 이벤트'를 생성해 RabbitMQ에 전송한다.
  2. RabbitMQ는 라우팅 키를 기반으로 올바른 큐에 해당 이벤트를 전송한다.
  3. 이벤트를 받은 MQ-SUB 서비스는 이벤트에 담겨있는 데이터인 "test message sent."라는 문자열을 출력한다.


6. 마무리

이번 게시글을 통해 메시지 큐의 개념과 RabbitMQ의 동작 방식에 대해서 알아봤습니다. 긴 글 읽어주셔서 감사드리고 도움이 되었으면 좋겠습니다..!

예제 프로젝트 : https://github.com/Kyu0/velog-example/tree/main/rabbitmq-example

잘못된 내용이나 오타 지적 언제나 환영입니다.

profile
개발자

0개의 댓글