들어가기 전

이번 포스팅에서는 RabbitMQ에서 AMQP를 활용하여 통신하는 방식과 AMQP의 5가지 프레임 유형에 대해서 알아보겠습니다.

 

 

RabbitMQ가 AMQP를 활용하여 통신하는 방법

RabbitMQ는 AMQP 메시지 브로커로 클라이언트와의 연결 수립 및 제어 과정에서 프로토콜 레벨의 명령–응답(Command–Reply) 방식의 통신을 사용합니다.

RabbitMQ은 일반적인 웹 기반 API의 통신 방식과 다릅니다.

일반적인 HTTP 기반 API에서는 클라이언트가 요청을 보내고, 서버는 해당 요청에 대한 응답만 반환합니다.

응답을 하고 서버가 클라이언트한테 다시 요청을 보내지 않습니다.

반면 AMQP에서는 클라이언트뿐만 아니라 서버 역시 메서드 프레임을 통해 명령을 전송할 수 있습니다.

AMQP로 통신을 시작할 때 클라이언트가 서버에 프로토콜 헤더를 전송합니다.

클라이언트로부터 요청을 받은 RabbitMQ는 Connection.Start 명령으로 응답하여 명령 및 응답 흐름을 시작하고 클라이언트는 Connection.StartOk 응답 프레임으로 RPC 요청에 응답합니다.

이 과정은 클라이언트와 브로커 간 프로토콜 레벨 RPC 통신을 성립시키기 위한 초기 핸드셰이크 과정입니다.

 

 

 

RabbitMQ에 접속하기 위한 start, tune, open 단계의 동기식 RPC 교환이 완료되면 클라이언트와 브로커 간의 AMQP Connection이 수립되며, 이후 채널을 생성해 애플리케이션 레벨 요청을 처리할 수 있는 상태가 됩니다.

 

 

AMQP 프레임 구조 및 컴포넌트

AMQP 스펙에는 C++, 자바와 같은 객체지향 프로그래밍 언어와 유사한 클래스와 메서드를 사용합니다.

클래스는 기능의 범위를 정의하고 각 클래스에는 서로 다른 작업을 수행하는 메서드가 있습니다.

연결 협상 과정에서 RabbitMQ 서버는 프레임으로 마샬링 된 Connection.Start 명령을 클라이언트에게 전송합니다.

여기서 Connection이 클래스이고 Start는 메서드를 나타냅니다.

RabbitMQ에서 AMQP 명령을 전송하거나 수신할 때 필요한 모든 인자들은 데이터 구조로 캡슐화된 프레임으로 인코딩 돼 전송됩니다.

프레임은 명령과 해당 인자를 인코딩해 각 프레임이 서로 구분되도록 하는 효율적인 방법입니다.

저수준 AMQP 프레임도 마찬가지로 포함하고 있는 내용을 서로 구분되도록 합니다.

저수준 AMQP 프레임은 아래 5가지 구성요소로 구성됩니다.

 

저수준 AMQP 5가지 구성 요소

  • 프레임 유형
  • 채널 번호
  • 프레임 크기(바이트)
  • 프레임 페이로드
  • 끝 바이트 표식(ASCII 값 206)

 

 

 

저수준 AMQP는 프레임 유형, 채널 번호, 프레임 크기 구성된 프레임 헤더로 시작합니다.

첫 번째 필드는 단일 바이트로 프레임 유형을 나타내고 두 번째 필드는 프레임이 속하는 채널을 지정합니다.

세 번째 필드는 프레임 본문의 크기를 바이트로 표현합니다.

마지막 필드에는 프레임 끝을 나타내는 바이트 마커가 있습니다.

마지막으로 프레임헤더와 마지막 바이트 마커 사이에 존재하는 프레임 페이로드는 5가지의 프레임 유형이 있는데 프레임 유형별로 내용이 다릅니다.

운반하는 내용을 무결성 있게 보호할 수 있도록 프레임 헤더와 마지막 바이트 마커 사이에 프레임 페이로드가 존재하도록 설계되었습니다.

 

프레임 유형 5가지

  • 프로토콜 헤더 프레임  : RabbitMQ에 연결하는 시점에 한 번만 사용합니다.
  • 메서드 프레임 : RabbitMQ와 서로 주고받는 RPC 요청이나 응답을 전달합니다.
  • 콘텐츠 헤더 프레임 : 메시지의 크기와 속성을 포함합니다.
  • 바디 프레임 : 메시지의 내용을 포함합니다.
  • 하트비트 프레임 : RabbitMQ와 연결된 클라이언트와 서버가 주고받으며 서로 사용가능한 상태인지 확인하는 데 사용합니다.

 

프로토콜 헤더, 하트비트 프레임은 일반적으로 클라이언트 라이브러리를 사용할 때 추상화 돼 라이브러리의 사용자에게는 직접 보이지 않지만 메서드, 콘텐츠 헤더, 바디 프레임은 RabbitMQ와 통신하는 애플리케이션을 작성할 때 구조가 표면적으로 보이고 RabbitMQ에 메시지를 발행할 때 사용됩니다.

 

AMQP의 하트비트 동작은 클라이언트와 서버가 서로의 요청에 응답하는지 확인하는 데 사용합니다.

RabbitMQ가 클라이언트 애플리케이션에 하트비트를 보냈는데 응답하지 않으면 RabbitMQ는 클라이언트와의 연결을 끊습니다.

단일 스레드 또는 비동기 개발 환경에서는 제한 시간을 약간 큰 값으로 늘립니다. 하트비트가 동작하기 어려운 환경에서 애플리케이션을 구동해 통신이 차단되는 경우 클라이언트 연결을 만들 때 하트비트 간격을 0으로 설정해 하트비트를 끌 수 있습니다.

rabbitmq.config 파일의 하트비트 값을 변경해 RabbitMQ의 최대 하트비트 간격 값을 변경할 수 있습니다.

 

첫 번째로 전송되는 프레임은 메서드 프레임으로 실행에 필요한 매개변수인 익스체인지(Exchange)와 라우팅 키(Routing key)를 함께 전송합니다.

다음으로는 콘텐츠 헤더와 바디 프레임으로 이어지는데 콘텐츠 헤더 프레임은 본문 크기와 함께 메시지 속성이 포함되어 있습니다.

바디 프레임메시지 본문이 AMQP의 최대 프레임 크기를 초과하면 여러 바디 프레임으로 분할됩니다.

위와 같이 항상 메서드 프레임 -> 콘텐츠 헤더 프레임 -> 하나 이상의 바디 프레임 순으로 전송됩니다.

 

 

 

위 그림을 보면 메서드 프레임에 Basic.Publish 명령이 전송된 다음 메시지의 내용 유형 및  메시지 속성이 포함된 콘텐츠 헤더 프레임이 이어집니다. 이 속성은  AMQP 스펙에 정의된 Basic.Properties 데이터 구조로 캡슐화됩니다.

마지막으로 메시지 내용이 적절한 수의 바디 프레임으로 마샬링 됩니다.

메서드 프레임, 콘텐츠 헤더 프레임의 내용은 데이터의 크기를 최소화하여 효율적으로 전송하기 위해 이진 데이터로 구성이 되어 있어 사람이 읽을 수 없습니다.

반면 바디 프레임은 내부의 전달되는 메시지 내용은 어떤 방식으로 압축되거나 인코딩 되어 있지 않아 일반 텍스트에서 이진 이미지 데이터에 이르기까지 다양한 형태로 저장될 수 있습니다.

 

 

지금까지 AMQP 프레임 구조 및 컴포넌트, 5가지 유형에 대해서 간단하게 알아보았습니다.
이제 프레임 유형 5가지 유형 중 프레임 헤더에 포함되는 메서드, 콘텐츠 헤더, 바디 프레임에 대해서 자세히 알아보겠습니다.

 

 

메서드 프레임

메서드 프레임은 RPC 요청이 처리할 클래스와 메서드 그리고 실행을 위한 인자를 함께 전달합니다.

Basic.Publish 명령을 전달하는 메서드 프레임에는 명령을 설명하는 바이너리 데이터와 함께 전달되는 요청 인자가 들어있습니다.

처음 두 필드는 숫자로 표현된 Basic 클래스와 Publish 메서드입니다. 그리고 익스체인지 이름, 라우팅 키가 문자열로 저장이 됩니다.

익스체인지와 라우팅 키는 RabbitMQ 서버에게 메시지 경로 지정 방법을 전달합니다. 마지막 mandatory 플래그는 RabbitMQ에게 메시지가 정상적으로 전달 됐는지 혹은 실패했는지 알려줍니다.

메서드의 프레임 페이로드의 각 데이터 값은 유형별로 서로 다른 포맷으로 인코딩 되는데 바이트 크기를 최소화하고 데이터 무결성을 보장하며 데이터 마샬링, 언마샬링을 가능한 한 빨리 처리하도록 설계되어 있습니다.

일반적으로 Basic.Publish RPC 요청을 사용해 메시지를 보내는 작업은 단방향 통신입니다. 메시지를 발행할 때 mandatory를 사용하는 경우 애플리케이션은 RabbitMQ에서 응답한 Basic.Return 명령을 수신해야 합니다. mandatory 플래그에 설정한 요구 사항을 충족시키지 못하면 Basic.Return 명령을 동일한 채널의 클라이언트에게 전송합니다.

 

 

 

콘텐츠 헤더 프레임

콘텐츠 헤더 프레임은 RabbitMQ의 메시지의 크기와 그 외 데이터를 전달합니다.

RabbitMQ 서버와 메시지를 수신하는 애플리케이션 사이에 주고받는 메시지를 설명하는 속성을 포함합니다.

이러한 속성들은 Basic.Properties 테이블의 값으로 메시지 내용을 설명하는 데이터가 포함되어 있거나 비어있을 수 있습니다.

대부분 클라이언트 라이브러리는 Content-Type 또는 Delivery mode와 같은 최소한의 필드는 미리 채웁니다.

메시지 속성은 메시지를 작성할 때 발행자와 구독자 간에 메시지 내용에 대한 약속을 만드는 데 사용될 수 있고 특별히 많은 양을 저장할 수. 있도록 설계되어 있습니다.

 

 

바디 프레임

전송되는 데이터 유형에 대해 독립적이며 이진 데이터 혹은 텍스트 데이터를 포함한다.

메시지는 JPEG 이미지나 Json 또는 XML 형식으로 직렬화 한 데이터를 전송할 수 있습니다.
데이터는 메시지 속성과 본문으로 구분돼 저장됩니다.

RabbitMQ에서 메시지 속성과 본문이 서로 의존적이지 않으므로 둘을 결합해서 다양한 유형의 데이터를 표현할 수 있게 됩니다.