RabbitMQ 톺아보기 1부
안녕하세요! 개발자 Jay입니다! 최근에 저의 관심사는 Event-driven, MSA입니다.
아무래도 MSA의 문제가 서비스가 잦은 호출로 인한 네트워크 과부하, 데이터 정합성, 트랜잭션 등에 문제가 있다 보니 Event-driven에 관심을 가지지 않을 수가 없었습니다.
이전 회사에서는 MSA 서비스간의 잦은 호출들은 gRPC를 통해 어느 정도 해결했으나 데이터 정합성과 트랜잭션에 대해서는 여전히 고민이 많았던 것 같습니다. 이런 점을 저 스스로 완벽하게 해결해 보지 않았고 이론, 실무적인 부분들에 대해 확실하게 알지 못하고 있어서 이번에 쉬면서 한번 스터디해보려고 마음을 먹었습니다!
그 첫번째가 RabbitMQ를 톺아보는 것입니다! RabbitMQ는 비동기 처리를 위해 Celery와 함께 자주 써보긴 했지만 완벽하게 알지는 못했던 것 같습니다. 그리고 Celery와 별개로 RabbitMQ를 Event-driven Message Broker로 사용해보기 위해 Pika라는 라이브러리를 함께 사용해 볼 예정입니다! (pika 사용은 2부에서!)
먼저 RabbitMQ가 뭐고 어떤 특징들을 가지고 있는지 알아보겠습니다!
(주의: RabbitMQ 공식문서를 참고하여 요약한 내용입니다. 잘못된 점이 있을수도 있으므로 제일 좋은 건 공식문서를 보는 게 최고입니다!)
1. RabbitMQ 란?
RabbitMQ는 오픈 소스 메시지 브로커 소프트웨어(메시지 지향 미들웨어)로서, AMQP를 구현하였으며 그 이후로 STOMP, MQTT 등의 프로토콜을 지원하기 위해 플러그인 구조와 함께 확장되고 있다.
메시지를 생산하는 생산자(Producer)가 메시지를 큐에 저장해 두면, 메시지를 수신하는 소비자(Consumer)가 메시지를 가져와 처리하는 Publish/Subscribe 방식의 메시지 전달 브로커이다. [출처: 위키백과]
RabbitMQ에 대한 내용 중 기본적으로 알아야 할 몇 가지 개념에 대해 정리해보겠습니다.
- AMQP(Advanced Message Queuing Protocol)
메시지 지향 미들웨어를 위한 개방형 표준 응용 계층 프로토콜입니다.
- Publisher
메시지 브로커에 메세지를 발행하는 주체입니다. 발행된 메세지는 Exchange를 통해 Queue에 전달됩니다.
- Consumer
메세지를 소비하는 주체입니다.
consumer에서는 push, pull 두 가지 방식이 있는데 일반적으로 Push 방식을 사용하고 권장하고 있습니다. push방식은 말 그대로 Message Brocker가 Consumer에게 메시지를 pushing하여 소비하는 방식입니다.
Pull방식은 Consumer에서 메세지를 일정 시간 pulling 해서 처리하는 방식이라고 합니다. pull방식은 아무래도 메시지가 없을 때도 지속적으로 네트워크 리소스를 사용하기 때문에(계속 pulling 해야 하므로) 매우 비효율적이라고 합니다.
* 알아볼것: Kafka는 반면에 pull방식을 사용하는 걸로 알고 있는데 아무래도 메시지를 처리하는 과정에서 push, pull의 장단점이 있을 것 같네요. 지금 당장 생각나는걸로는 pull방식을 하게되면 Counsumer가 다음 메세지를 처리할 준비가 되었을때만 메세지를 가져올 수 있을것 같다는... 생각
- Exchange
Publicher를 통해 메시지를 수신하여 Binding규칙을 통해 Queue로 메세지를 전달합니다. exchange type에 따라 Queue에 어떻게 메세지를 전달할지 정합니다.
Exchanges then distribute message copies to queues using rules called bindings.
실제 메세지를 queue넣는 대신 메세지에 대한 참조가 queue에 저장되게 되어 메모리를 적게 소비하다고 하네요.
exhange는 네 가지 타입을 가지고 있습니다.
1) Direct exchange
Direct exchange는 rounting key 기반으로 메시지를 전달합니다. 1:1 방식으로 전달되는 Unicate routing 방식에 이상적입니다. (Multicast routing에도 사용될 수 있음)
2) Fanout exchange
Fanout exchange는 binding 된 모든 큐에 메시지를 전달됩니다. routing key는 무시됩니다. 메세지 브로드캐스팅 라우팅에 적합한 방식입니다. routing key를 평가할 필요가 없기 때문에 성능적인 이점이 있습니다.
3) Topic exchange
Topic exchange는 routing key의 패턴으로 메세지를 전달하는 방식입니다. 이 방식은 다양한 Pub/Sub 패턴에서 유용하게 사용할 수 있습니다. Multicast routing에서 일반적으로 사용됩니다.
4) Header exchange
메세지 헤더를 사용하여 메세지 속성에 대한 라우팅을 routing key보다 더 쉽게 할 수 있습니다. Header exchange는 routing key 속성을 무시합니다. 대신 라우팅에 사용되는 속성을 headers 속성에서 가져옵니다. header값이 binding시 지정한 값과 같으면 메시지가 일치하는 것으로 간주합니다.
x-match argument를 통해서 header 매칭에 이용합니다.
- x-match = any 일 경우 헤더 테이블 값 중 하나가 연결된 값 중 하나와 일치하면 메시지 전달
- x-match = all 일 경우 모든 값이 일치해야 메시지를 전달
Header exchange는 '강력한 Direct exchange'로 볼 수 있습니다. header 값을 기반으로 라우팅 하기 때문에 문자열일 필요가 없는 Driect exchange로 사용할 수 있습니다. 예를 들어 integer 혹은 hash(dictionary)가 될 수 있습니다.
- Binding
메시지를 Queue로 전달하기 위해 라우팅 하는 규칙입니다. Exchange와 Queue를 연결해주고 routing key를 이용해 메세지를 필터링할 수 있습니다.
- Queue
애플리케이션에서 사용하는 메시지들을 저장하는 역할을 합니다. Queue는 Exchange와 일부 속성을 공유하지만 추가적인 속성도 있습니다.
2. RabbitMQ docker로 실행해보기
RabbitMQ Docker hub에 접속하시면 도커 이미지 버전을 확인할 수 있습니다. 저 같은 경우는 GUI툴을 사용하기 위해 management 버전을 사용합니다. management버전이 아닌 경우 cli명령어로 일일이 해야 하기 때문에 다소 불편할 수 있습니다.
version: '3.1'
networks:
app-tier:
driver: bridge
services:
rabbit1:
image: rabbitmq:3.8-management
container_name: rabbit1
hostname: rabbit1
ports:
- "5671:5671"
- "5672:5672"
- "15671:15671"
- "15672:15672"
volumes:
- "본인 환경에 맞는 로컬 경로":/var/lib/rabbitmq
environment:
- RABBITMQ_DEFAULT_USER="디폴트 사용할 유저"
- RABBITMQ_DEFAULT_PASS="디폴트 사용할 비밀번호"
- RABBITMQ_DEFAULT_VHOST=default
networks:
- app-tier
|
cs |
docker-compose 파일 작성을 위처럼 작성한 후 docker compose up -d 명령어로 컨테이너를 실행시킵니다. 127.0.0.1:15672로 접속해서 설정한 유저, 비밀번호로 접속하면 RabbtMQ GUI를 만날 수 있습니다!
3. 마치며
이번 포스팅에서는 RabbitMQ가 뭐고 어떻게 구성되어 있는지에 대해 간단히 알아봤습니다.
다음에는 pika 라이브러리를 사용해서 실제로 애플리케이션을 구현해보도록 하겠습니다!
참고자료
https://www.rabbitmq.com/documentation.html
https://hwannny.tistory.com/82
https://medium.com/@sirajul.anik/easy-peasy-rabbitmq-squeezy-820b1c632465