RabbitMQ 원리
RabbitMQ는 많은 서비스에서 사용하고 있는 메시지 큐 시스템입니다. 어떤 원리로 동작하고 있을까요?
RabbitMQ는 많은 서비스에서 사용하고 있는 메시지 큐 시스템입니다.
🤔그러면 RabbitMQ는 어떤 원리로 동작을 하고 있을까요?
RabbitMQ는 AMQP 0-9-1 프로토콜로 통신하고 있습니다. AMQP(Advanced Message Queuing Protocol)란 클라이언트 어플리케이션(프로듀서)이 메시지 미들웨어 브로커와 통신할 수 있도록 하는 메시지 프로토콜입니다.
브로커
프로듀서에서 AMQP 통신으로 RabbitMQ서버로 메시지을 보내면 브로커가 컨슈머에게 라우팅하여 보내는 역할을 하고 있습니다.
교환(Exchange)
프로듀서에서 메세지를 보내면 브로커의 Exchange로 보내집니다. Exchange는 Exchange Type을 확인하여 Queue에 메시지 복사본을 라우팅하는 역할을 합니다.
Exchange를 생성할 때는 속성들을 시스템을 고려하여 설정을 해야합니다.
- Durability(내구성): 브로커가 재시작 이후에도 Exchange는 유지됩니다.
- Durable: 재시작 이후에도 유지 (Disk에 저장)
- Transient: 재시작 이후에도 삭제 (Memoy에 저장)
- Auto Delete: 마지막 큐가 바인딩을 해제하면 삭제됩니다.
대기열(Queue)
메시지를 Queue에 저장하고 있고 선입선출(FIFO) 방식으로 컨슈머에게 전달하는 역할을 하고 있습니다. 컨슈머가 메시지를 받아 소비를 했다고 보내면(message acknowledgements) 메시지는 Queue에서 삭제 됩니다.
Queue를 생성할 때에도 속성들은 아래와 같습니다.
- Durability(내구성): 브로커가 재시작 이후에도 Queue는 유지됩니다.
- Durable: 재시작 이후에도 유지 (Disk에 저장)
- Transient: 재시작 이후에도 삭제 (Memoy에 저장)
🤔 durable로 설정 했을 때는 transient보다 느릴까요?
duable로 했을 때 큐에 대한 메타데이터, 메시지 데이터를 rabbitmq 내장 디스크 DB인 mnesia에 저장하고 있어서 I/O가 없는 메모리에 저장하는 transient가 빠릅니다. 하지만, 삭제 되면 안되는 메시지 데이터는 durable로 설정하여 신뢰성을 보장하면 됩니다. - Exclusive(독점): 하나의 연결에서만 사용되며 연결이 닫힐 때 큐가 삭제
- Auto Delete: 최소 하나 이상의 소비자를 가졌던 큐가 마지막 구독을 해제하면 삭제
바인딩(Bindings)
Exchange와 Queue가 라우팅할 때 사용하는 규칙을 말합니다. Exchange Type 중에 Direct Type은 특정 큐와 라우팅하기 위해서는 라우팅키가 필요한데 이를 Binding이라고 합니다.
컨슈머
큐에 있는 메시지를 받을 수 있는 어플리케이션을 말합니다.
메시지 확인(Message Acknowledgements)
컨슈머가 메시지를 처리를 못하거나 서버 연결이 끊겼을거나 여러 가지 이유로 실패할 수 있습니다. 이러한 이유로 큐에서 메시지를 제거해야할 시점을 컨슈머가 제어할 수 있도록 권한을 부여합니다.
메시지 확인에 대한 두가지 모드
- 자동 확인(automatic acknowledgement): 브로커가 컨슈머에게 메시지를 보내자마자(
basic.deliver또는basic.get-ok메서드사용) 메시지를 제거합니다. 자동 확인 모드는 컨슈머가 메시지를 실제 처리했는지 여부와 상관없이 컨슈머에게 보냈다는 사실만으로 메시지를 제거합니다. - 명시적 확인(explicit acknowledgement): 컨슈머가
basic.ack메소드를 사용하여 브로커에게 응답을 보내야지만 메시지를 제거합니다. 컨슈머가 메시지 처리에 대한 원하는 시점에 응답을 보내여 신뢰성을 확보 할 수 있습니다.
만약 메시지를 소비할 컨슈머가 다운되면 다른 컨슈머에게 보내거나 컨슈머가 없는 경우 컨슈머가 등록될 때 까지 기다렸다가 보냅니다.
메시지 거부(Rejecting Messages)
컨슈머가 메시지를 처리하다가 실패했을 때 메시지 확인과 다른 방식으로 응답을 보낼 수 있습니다.
컨슈머는 basic.reject 메소드를 사용하여 메시지를 거부할 수 있습니다. 메소드를 사용할 때 두가지 옵션 중 하나를 선택하여 보낼 수 있습니다.
- 메시지 폐기: 메시지를 큐에서 삭제하도록 요청합니다.
- 다시 큐에 넣기: 브로커에게 다른 컨슈머에게 보내거나 다른 컨슈머가 없을 경우 나중에 다시 전달하도록 요청합니다. 이 옵션은 실패한 메시지를 계속 넣을 수 있기 때문에 무한루프가 발생하지 않도록 주의해야합니다.
일괄처리하는 컨슈머에 경우 부정적 확인(Negative Acknowledgements)을 통해 여러 메시지를 거부 할 수도 있습니다.
메시지 미리 가져오기(Prefetching Messages)
컨슈머가 다음 확인 응답을 보내기 전까지 한번에 받을 수 있는 메시지 최대 개수를 말합니다. 이 기능을 사용하면 간단한 부하분산이나 처리량 개선할 수 있습니다. 브로커는 컨슈머가 메시지 확인 응답을 보낼 때 까지 컨슈머에게 더이상 보내지 않거나 설정된 최대 개수만큼 더 보냅니다.
메시지 미리 가져오기는 채널수준에서 지원하며 연결(connection)에 대해서는 기능을 지원하지 않습니다.
메서드(Methods)
메서드는 HTTP 메서드와 같은 작업(operations)를 의미합니다. 객체 지향 프로그래밍의 메서드와는 다른 의미이며 어플리케이션이 브로커와 통신하며 특정 작업을 수행 할 수 있도록 합니다.
메서드는 클래스라는 논리적인 그룹으로 묶여있습니다. Exchange 클래스에는 해당 메서드들이 포함되어 있습니다. exchange.declare exchange.declare-ok exchange.delete exchange.delete-ok
대부분 메서드는 요청과 응답으로 쌍을 형성합니다. 예를 들어, 프로듀서나 컨슈머가 브로커에게 새로운 Exchange을 선언 요청(exchange.declare)보내면 브로커에서 작업이 끝난 후 응답(exchange.delcare-ok )합니다. basic.publish 는 응답이 없는 메서드이며 쌍을 형성하지 않습니다.
이러한 메서드들을 사용하여 작업을 수행할 수 있기 때문에 AMQP 0-9-1는 프로그래밍 가능한 프로토콜입니다.
연결(Connections)
어플리케이션(프로듀서나 컨슈머)이 브로커와 상호작용하고 AMQP 엔티티(Exchange, Queue, Binding)에 대한 작업을 수행할려면 연결이 필요합니다. 어플리케이션과 브로커 간의 TCP를 사용하여 물리적인 네트워크 링크 역할을 합니다.
채널(Channels)
하나의 연결(TCP)을 공유하는 경량 연결(lightweight connections)로 여러 개의 독립적인 통신 경로를 만드는 방법입니다. 많은 TCP 연결을 하면 시스템 성능에 영향을 미칠 수 있습니다.
채널을 통해 메서드를 사용하여 어플리케이션과 브로커간의 모든 상호작용이 이루어집니다. 어플리케이션은 여러 스레드/프로세스마다 채널을 열고 작업을 수행할 수 있습니다.
가상 호스트(Virtual Host)
브로커 내에서 여러개의 독립적인 환경을 호스팅할 수 있게 해줍니다. AMQP 엔티티가 격리된 환경에서 존재하도록 만들어 운영됩니다.
어플리케이션은 브로커와 연결을 시도 할 때 사용할 호스트(vhost) 지정합니다.


