본문 바로가기

2.분석 및 설계/Network

STOMP 프로토콜 규약

이글은 "STOMP Protocol Specification, Version 1.2" 내용을 정리하고 요약하였다.

작성자: ospace114@empal.com, http://ospace.tistory.com/

들어가기

STOMP은 간단한 상호운영가능한 프로토콜로 비동기 메시지로 설계되었다. STOMP은 스크립트 언어로부터 엔터프라이즈 메시지 브로커 연결을 위해서 태어났다. 다른 대안으로는 AMQP같은 오픈 메시지징 프로토콜이 있다. STOMP은 HTTP에서 모델링된 프레임기반 프로토콜이다. 프레인은 명령, 옵션 헤더들, 옵션 바디로 구성된다. 기본 인코딩은 UTF-8이지만, 바디에는 다른 인코딩을 사용할 수 있다. pub-sub 형태로 클라이언트는 producer 또는 consumer로 동작합니다. STOMP은 메시지를 담는 그릇으로 구체적인 구현 및 의미체계는 구현에 종속되어 있는 자유로운 형태입니다.

STOMP 프레임

STOMP인 TCP 같은 신뢰할 수 있는 프토코콜 기반ㅡ로하는 프레임 기반 프로토콜이다.

프레임 메시지 구조

COMMAND
header1:value1
header2:value2

Body^@

각 문자열 끝은 EOL로 종료된다. 커맨드 다음에 키값 쌍형태로 여러 헤더가 구성된다. 다음에 빈줄로 구분된 바디가 온다. 그리고 "^@"(control+@)으로 메시지 종료를 한다.
커멘드와 헤더는 UTF-8로 인코딩되었고, 하위 호환성으로 인해 CONNECT와 CONNECTED 프레임을 제외하고 UTF-8 인코딩된 헤더에 CR, LF, : 문자들은 이스케이프처리 된다. (CR은 \n, LF은 \n, :은 \c, \은 \) 그외 인스케이프 문자를 에러 처리된다.

바디는 SEND, MESSAGE, ERROR 프레임에서만 사용된다.

표준 헤더

헤더가 반복되는 값이 있는 경우 첫번째 값만 사용해야 한다. 이후 값들은 이력 정도로 사용될 수 있다.
서버에서는 단일 프레임에 헤더 개수, 헤더 길이, 바디 길이를 제한할 수 있다. 이 수치를 초과하면 ERROR 프레임을 발생시킨다.

  • content-length: 바디의 옥텟 개수
  • content-type: 바디 해석을 위한 MINE 타입, 없다면 바이너리로 간주
  • receipt: CONNECT 제외한 모드 프레임에서 사용 가능. 자세한 부분은 RECEIPT 프레임 참조.

연결

스트림이나 TCP 연결을 초기화한다.

CONNECT 프레임

CONNECT 프레임으로 연결 요청을 한다. 하위 호환성으로 STOMP 프레임도 동일하게 처리한다. 버전 1.2에서는 accept-version과 host 헤더를 설정한다. accept-version은 클라이언트가 지원하는 STOMP 버전으로 서버에서는 version으로 수락된 버전을 반환, host은 클라이언트가 연결 요청하는 서버의 가상 호스트 이름이다. 그외에 설정할 수 있는 헤더로 login(사용자ID), passcode(패스워드), heart-beat가 있다.

아래는 간단한 시퀀스이다. 직관적이라 별다른 설명이 필요없다.

CONNECT
accept-version:1.0,1.1,1.2
host:stomp.foo.org

^@
CONNECTED
version:1.1

^@
ERROR
version:1.2,2.1
connect-type:text/plain

Supported protocol versions are 1.2 2.1^@

클라이언트 프레임

  • SEND
  • SUBSCRIBE
  • UNSUBSCRIBE
  • BEGIN
  • COMMIT
  • ABORT
  • ACK
  • ACK
  • DISCONNECT

SEND 프레임

메시지를 전송하며 메시지 어디로 전송할지 지정하는 필수 헤더인 destination가 있다.

SEND
destination:/queue/a
content-type:text/plain

hello queue a^@
SEND
destinatin:/queue/trade
content-type:application/json
content-length:44

{"action":"BUY", "ticker":"MMM", "shares":44}^@

추가로 transaction헤더를 지원한다. content-length와 content-type을 사용할 수 있다.

SUBSCRIBE 프레임

주어진 destination을 수신하기 위해 등록에 사용.

SUBSCRIBE
id:sub-1
destination:/queue/*

^@

id헤더는 여러 가입에 대해 식별하기 위해 사용한다. 나중에 MESSAGE나 UNSUBSCRIBE 프레임에서 사용된다. 추가로 ack헤더로 메시지 승인 모드를 제어한다. auto, client, client-individual 값을 사용한다. auto는 클라이언트가 서버로 ACK 프레임을 보낼 필요 없다. 서버는 전송 즉시 수신했다고 가정한다. client은 클라이언트가 반드시 서버로 ACK 프레임을 보낸다. 서버가 ACK를 받지 못하면 전송실패로 간주한다. 서버는 클라인트 ACK를 이전 메시지에 수신을 승인하는 누적승인형태로 작동한다. 클라이언트가 일부 메시지를 처리하지 않은 경우 NACK 프레임으로 서버에 알려야한다. client-individual은 누적승인을 빼고 client와 동일하다.

UNSUBSCRIBE 프레임

기존 가입을 제거. id헤더는 필수로 SUBSCRIBE 프레임의 값을 사용한다.

UNSUBSCRIBE
id:sub-1

^@

ACK

메시지를 처리하고 승인할 때 사용. 반드시 id헤더를 사용해서 승인된 MESSAGE의 ack 헤더와 일치하는 id 값을 포함한다. transaction 헤더를 사용해서 메시지 승인을 트랜잭선 처리 일부로 사용할 수 있다.

ACK
id:sub-1
transaction:tx1

^@

NACK

ACK의 반대. 클라이언트에서 메시지를 처리하지 않았다고 서버로 알림.
id 헤더와 transaction 헤더는 ACK와 동일

DISCONNECT

연결을 종료할 때 사용. 이전 전송된 프레임에 대한 보장은 하지 않음.
정상적인 종료하기 위해서는 클라이언트는 다음과 같이 처리한다.

DISCONNECT
receipt:77

^@
RECEIPT
receipt-id:77

^@

클라이언트는 받드시 더 이상 보낼 프레임이 없을때 종료.

그외프레임

트랜젝션 관련해서 BEGIN, COMMIT, ABORT가 있다. transaction헤더로 각 트렌젝션을 식별하여 처리한다.

서버 프레임

  • MESSAGE
  • RECEIPT
  • ERROR

MESSAGE

클라이언트로 메시지를 전달하는데 사용. destination 헤더는 필수이며, 메시지 도착지를 명시한다. STOMP를 사용한다면 SEND 프레임의 destination 헤더 값과 동일하면 된다. message-id 헤더도 필수이며, 메시지 ID로 사용된다. subscription 헤더는 메시지를 수식하는 구독 식별자와 일치시킨다. 구독으로부터 수신된 ACK가 필요한 메시지에 대해 MESSAGE 프레임은 반드시 ack 헤더를 포함해야 한다.

MESSAGE
subscription:0
message-id:007
destination:/queue/a
content-type:text/plain

hello queue a^@

그리고 바디가 있다면 content-lenght과 content-body가 필요하다.
그외 사용자 정의 헤더도 추가할 수 있다.

RECEIPT

RECEIPT는 서버에서 클라이언트로 보내는 프레임이다. 이는 클라이언트가 receipt를 요구한 프레임을 서버가 처리한 경우에 보내진다. receipt-id가 필수이고 receipt를 요구한 프레임에 receipt 헤더 값을 사용한다.
RECEIPT 프레임은 서버에 의해 해당 클라이언트 프레임을 처리했다고 승인했다는 의미이다. STOMP는 스트림 기반이면 이전 모든 프레임에 대한 누적 승인으로 처리할수 있다. 클라이언트 연결이 종료되고 이전에 받은 프레임은 계속 처리된다.

ERROR

서버에서 잘못처리면 ERROR 프레임으로 전송하고 연결을 끝는다. message 헤더가 필수이며, 간단한 에러를 기술한다. 바디에는 자세한 정보를 포함할 수 있다.

ERROR
receipt-id:message-12345
content-type:text/plain
content-length:170
message:malformed frame received

The message:
===
MESSAGE
destined:/queue/a
receipt:message-12345

Hello queue a!
===
Did not contain a destination header, which is REQUIRED
for message propagation.
^@

BNF

NULL                = <US-ASCII null (octet 0)>
LF                  = <US-ASCII line feed (aka newline) (octet 10)>
CR                  = <US-ASCII carriage return (octet 13)>
EOL                 = [CR] LF 
OCTET               = <any 8-bit sequence of data>

frame-stream        = 1*frame

frame               = command EOL
                      *( header EOL )
                      EOL
                      *OCTET
                      NULL
                      *( EOL )

command             = client-command | server-command

client-command      = "SEND"
                      | "SUBSCRIBE"
                      | "UNSUBSCRIBE"
                      | "BEGIN"
                      | "COMMIT"
                      | "ABORT"
                      | "ACK"
                      | "NACK"
                      | "DISCONNECT"
                      | "CONNECT"
                      | "STOMP"

server-command      = "CONNECTED"
                      | "MESSAGE"
                      | "RECEIPT"
                      | "ERROR"

header              = header-name ":" header-value
header-name         = 1*<any OCTET except CR or LF or ":">
header-value        = *<any OCTET except CR or LF or ":">

참고

[1] STOMP Protocol Specification, Version 1.2, http://stomp.github.io/

반응형