💡 해당 페이지의 모든 내용은 RabbitMQ 공식문서를 참고하였습니다. https://www.rabbitmq.com/docs/publishers
이번 시간에는, RabbitMQ의 프로토콜들과, Channel, Exchange 개념을 알아보면서 어떤식으로 Producer가 Broker에게 메시지를 전달하고, 메시지의 안정성을 유지하는지 알아봅시다.
Ack
기능RabbitMQ에서는 네 가지의 프로토콜을 지원합니다. 특징이 약간씩은 다르지만, 기본적으로 body
와 header
값을 가지고 있는 것은 동일합니다. (HTTP의 Body와 Header의 느낌과 비슷합니다)
하지만 이보다 제일 중요한 점은 Acknowledgement mechanism
이 존재한다는 것입니다. Publisher 가 메시지를 Broker에게 전달했는지, Consumer가 Broker로부터 메시지를 전달 받았는지를 확인할 수 있는 메카니즘입니다. 이를 통해 데이터의 안전성을 보장할 수 있습니다. ( TCP Handshake 과정의 Ack에 영감을 받아 해당 메카니즘을 개발하였다고 합니다)
저희 동아리에서는 AMQP
프로토콜을 사용합니다. 앞으로의 내용은 AMPQ
프로토콜과 관련된 내용만 다룰 것이니, 다른 프로토콜과의 차이점을 자세히 알고 싶으시면 아래 내용을 참조해주세요. (RabbitMQ는 본래, AMPQ를 지원하기 위한 목적으로 만들어졌다고 합니다)
참고 : 공식 docs
참고 : 공식 docs
가장 먼저 알아볼 것은 Channel
입니다. Message Queue를 사용하기 위해서는 Message Broker와의 Connection연결이 이루어져야 할 것입니다. 수많은 비즈니스 로직을 처리하기 위해서 수많은 Connection이 필요할 수도 있습니다. (하지만 우리 서비스에는 그렇게 많은 Connection이 필요하다고 생각되진 않긴 합니다)
여기서 Channel이 요긴한 점은, 하나의 TCP 커넥션을 계속 사용할 수 있다는 점입니다. 이를 통해 하나의 TCP Connection에서 여러개의 Channel을 통해 Broker와의 통신이 가능해집니다.
참고 : cloudamqp.com 블로그
하지만 경우에 따라서, 무작정 Channel을 사용하는 것보다, Connection을 사용하는 편이 더 나을 수도 있습니다. 이는 성능 테스트를 통해서 튜닝 포인트로 가져 가야할 지점이라고 생각합니다.
Channel을 사용하게 된다면? Channel의 Max 수를 제한하는 튜닝 지점이 생깁니다. 현재 동아리에서는 10개의 Max Channel을 설정하였습니다. (Iris 서버에서는 Channel 수의 제한이 있지만, Backend 서버에서는 이러한 제한이 없습니다 이유는 모르겠네요..)
Connection을 사용하게 된다면? 통상 One Thread에 One Connection을 사용하게 됩니다. 또한 이러한 Connection의 Max값을 설정하여 Connection 수를 제한합니다.
동아리 백엔드의 프레임워크가 Nest.js인 점을 고려해보면, 이벤트 루프 기반의 단일 쓰레드 아키텍쳐이므로, 하나의 쓰레드와 MQ를 연결하는 Connection만으로 요청별로 Channel을 만들어 송수신이 가능하지 않을까 싶습니다.
즉 정리하자면
하나의 Connection에서 여러 Channel을 사용할 것인지(동시 요청 수에 적합한 Channel 수를 채택하면서)
요청별로(쓰레드별로) Connection을 만들 것인지 결정해야 합니다.
1번을 사용할 시에는, Resource 사용량이 감소하는 대신, 한번에 Concurrent하게 요청을 전송하기 때문에, 메시지 전송 순서와 관련한 동기화 문제에 신경을 써야합니다. 2번을 사용할 시에는, Resource 사용량이 늘어나는 대신, 동기화 문제를 신경쓰지 않아도 될 것입니다.
참고 자료
Channel 과 Connection 차이
Channels and Connections in RabbitMQ | Baeldung
Channel을 사용하면 좋은 점
RabbitMQ -Connection vs Channel
RabbitMQ와 서버끼리 Channel이 연결되면 통신을 위한 기본적인 세팅은 완료가 되었습니다. 통신을 위한 준비가 되었으니, 메시지를 보내고, 해당 메시지를 어떻게 Queue에 저장할 것인지에 대해 알아봅시다.
기본적으로 Producer가 Message를 Publish하면 Broker에게 전송될 것입니다. 이 때, 가장 먼저 Exchange를 거치게 됩니다. Broker의 Exchange를 통해 해당 Message가 어떤 Queue에게 전달될 것인지를 결정합니다. (다양한 Exchange Type은 공식문서 참고) 마치 알맞은 Queue에 메시지를 전달하는 Routing Table 역할을 한다고 이해하시면 좋습니다.
그래서 Broker에게 올바른 Routing 정보를 주기 위해, Publisher 측에서는 여러 속성들이 포함된 메시지를 Broker에게 전달해야 합니다.
this.amqpConnection.publish(EXCHANGE, SUBMISSION_KEY, judgeRequest, {
messageId: String(submission.id),
persistent: true,
type: PUBLISH_TYPE
})
위의 처럼, Publish할때 Exchange
를 비롯하여 RoutingKey의 SUBMISSION_KEY
(우리의 MQ는 Exchange Type으로 Routing Key
를 사용하고 있음을 알 수 있습니다.), 메시지 정보인 judgeRequest
와 함께 Publish의 Option들을 전달하여 Message Queue에게 전달하는 것을 알 수 있습니다.
아래 API 공식문서를 보면 더 쉽게 이해할 수 있을 것입니다.
amqplib | Channel API reference
또한, Publish를 RabbitMQ 콘솔에서 테스트할 때,
위와 같이 테스트를 합니다. Header 값에는 messageId
를 넣어서, 채점 서버에서 제출 번호별로 채점을 돌리고 결과를 API서버에 반환합니다.
또한 Delivery Mode
는, 필수 옵션으로써, 코드에서도 반드시 명시해야하는 옵션입니다. 해당 옵션은 RabbitMQ가 restart되더라도 Message를 삭제하지 않게하는 옵션입니다.
type
의 경우는 크게 신경 쓰지 않아도 될 듯 합니다.
이 때, 이러한 Options들이 RabbitMQ도 알아야 하는 Option이 있을 것이고, RabbitMQ는 모르더라도, Consumer가 알아야 하는 정보들이 있을 것입니다. 이 중, Delivery Mode
경우는 Broker도 알아야 하는 정보이므로 반드시 포함시켜야 합니다. 이를 통해 메시지를 안정적으로 유지할 수 있기 때문입니다.