在安裝stomp擴展時, 有這樣的提示 error: 'zend_class_entry' has no member named 'default_properties'
交待下安裝上下文, stomp 版本是 1.0.3 而最新的是 1.0.8 php 版本是5.4.x , 猜想可能是由於版本差異造成的, 因為1.0.3的 stomp 出現的年份是2010年...
於是網上搜索了一下, 文章點這 說在出錯的文件中把 default_properties 改成 default_properties_table 就行了, 於是, 照做, 果不其然, 接下來的 make make install 都沒有出錯了.
擴展知識:
消息處理利器 ActiveMQ 的介紹 & Stomp 協議的使用
stomp 是什么? <轉自 http://simlegate.com/2013/10/17/stomp-specification-1.2/ >
STOMP協議規范
原文: STOMP Protocol Specification, Version 1.2
摘要
STOMP是一個簡單的可互操作的協議, 被用於通過中間服務器在客戶端之間進行異步消息傳遞。它定義了一種在客戶端與服務端進行消息傳遞的文本格式.
STOMP已經被使用了很多年,並且支持很多消息brokers
和客戶端庫。這個規范定義STOMP 1.2
協議以及對1.1
版本的更新。
發送反饋到stomp-spec@googlegroups.com
.
概述
背景
由於需要用腳本語言如Ruby
, Python
, Perl
去連接企業級的消息brokers
, STOMP產生了.在這種情況下,STMOP實現了一些簡單的操作,比如可靠地發送單一的消息,然后斷開或者從目的地消費所有消息。
STOMP是除AMQP
開放消息協議之外地另外一個選擇, 實現了被用在JMS brokers中特定的有線協議,比如OpenWire
. 它僅僅是實現通用消息操作中的一部分,並非想要覆蓋全面的消息API.
STOMP目前已經是個成熟的協議,在wire-level
方面, 它提供了一些簡單的用例,但仍保持其核心設計原則:簡單性和互操作性。 ### 協議概述
STOMP是基於frame的協議, 與HTTP的frame相似.一個frame
包含一個command
,一系列可選的headers
和body
.STOMP雖然是基於消息但同於也允許傳遞二進制消息。STMOP的默認消息格式是UTF-8
,但是在消息體中同樣支持其他格式編碼。
STOMP服務器就好像是一系列的目的地, 消息會被發送到這里。STOMP協議把目的地當作不透明的字符串,其語法是服務端具體的實現。 此外STOMP沒有定義目的地的交付語義是什么。 交付,或“消息交換”,語義的目的地可以從服務器到服務器,甚至從目的地到目的地。這使得服務器有可創造性的語義,去支持STOMP。
STOMP client的用戶代理可以充當兩個角色(可能同時): * 作為生產者,通過SEND
frame發送消息到server * 作為消費者,發送SUBSCRIBE
frame到目的地並且通過MESSAGE
frame從server獲取消息。
STOMP版本之間的變化
STOMP 1.2 大部分向后兼容1.1. 有兩點不兼容的改變: * 用回車加換行符代替只用換行符結束frame * 簡化了消息應答,用專用的header
除此之外,STOMP 1.2並沒有增加新特性,而是闡述規格中的一些模糊概念,比如: * 重復的frame header條目 * content-length
和content-type
headers的用法 * 必須支持servers STOMP frame * 連接延遲 * 作用域,訂閱的唯一,事務的標示符 *RECEIPT
frame的含義
設計哲學
簡易性,互通性是STOMP主要設計哲學.
STOMP被設計成為輕量級的協議,它很容易用其他語言在client和server實現。這就意味着servers的架構沒有太多的約束,以及沒有太多的特性比如目的地命名空間,可靠的語法需要去實現。
在這份規格書里面,注意,我們沒有明確定義的STOMP 1.2 servers特性。你應該查閱STMOMP servers 文檔去獲得這些特性的詳細描述。
一致性
RFC 2119中詳細地解釋了MUST
, MUST NOT
, REQUIRED
, SHALL
, SHALL NOT
, SHOULD
,SHOULD NOT
, RECOMMENDED
, MAY
, 和 OPTIONAL
這些關鍵字
為了阻止來自服務端地攻擊,保護內存溢出,消除平台限制,限制了不受約束的輸入。
規格中一致性的級別適用於STOMP clients and STOMP servers.
STOMP Frames
STOMP是基於幀的協議,它假定底層為一個2-way的可靠流的網絡協議(如TCP)。客戶端和服務器通信使用STOMP幀流通訊。幀的結構看起來像:
COMMAND
header1:value1
header2:value2
Body^@
幀以command字符串開始,以EOL結束,其中包括可選回車符(13字節),緊接着是換行符(10字節)。command下面是0個或多個<key>:<value>
格式的header條目, 每個條目由EOL結束。一個空白行(即額外EOL)表示header結束和body開始。body連接着NULL
字節。本文檔中的例子將使用^@
,在ASCII中用control-@表示,代表NULL字節。NULL字節可以選擇跟多個EOLs
。欲了解更多關於STOMP幀的詳細信息,請參閱Augmented BNF節本文件。
本文檔中引用的所有command 和header 名字都是大小寫敏感的.
編碼方式
commands和headers 都是用UTF-8編碼的.在用UTF-8編碼的headers中除了CONNECT
和CONNECTED
幀以外,任何的回車符,換行符,colon found(?)都將被轉義.
轉義的目的在於允許header中的鍵值包含那些把octets
當作值的frame header.
為了向后兼容STOMP 1.0, CONNECT
和CONNECTED
不會轉義回車符,換行符,colon found(?)
C風格的字符串轉義被用在UTF-8編碼的headers中去轉義回車符,換行符以及colon found.當解碼headers時,必須使用下列轉換: * \r (octet 92 and 114) translates to carriage return (octet 13) * \n (octet 92 and 110) translates to line feed (octet 10) * \c (octet 92 and 99) translates to : (octet 58) * \\ (octet 92 and 92) translates to \ (octet 92)
未定義轉義序列如\t
(octet 92 and 116)必須被視為一個致命的錯誤。相反,當編碼幀頭,必須使用逆轉變.
The STOMP 1.0 specification included many example frames with padding in the headers and many servers and clients were implemented to trim or pad header values. This causes problems if applications want to send headers that SHOULD not get trimmed. In STOMP 1.2, clients and servers MUST never trim or pad headers with spaces.
Body
只有SEND
, MESSAGE
, 和ERROR
幀有body。所有其他的幀不能
有body。
標准header
大多數被用的header都有特殊的含義。
Header content-length
所有的幀可能都包括有content-length
的header。它定義了消息體的大小。如果header包含了content-length
, 包含空字節的消息體的最大字節數不能超過這個數. 幀仍然需要以空字節結束。
如幀體存在,SEND
, MESSAGE
和 ERROR
幀應該包含content-length
.如果幀體包含空字節,那么這個幀必須包括content-length
.
Header content-type
如果幀體存在,SEND
, MESSAGE
和 ERROR
幀應該包含content-type
幫助接受者去理解幀體.如果設置了content-type
, 它的值必須是描述幀體格式的MINE
類型.否則,接收者應該認為幀體格式為二進制Blob.
以text/
開頭的MINE類型的默認文本編碼是UTF-8
. 如果你正在用一個基於MINE類型的不同編碼, 你應該添加;charset=<encoding>
MINE類型。例如:如果你發送一個UTF-16編碼的HTML body, 應該設置text/html;charset=utf-16
. ;charset=<encoding>
也能添加到任何非text/
MINE類型后去作為說明。UTF-8編碼的XML是個很好的例子。它的編碼被設置為application/xml;charset=utf-8
.
所有STOMP客戶端和服務端必須支持UTF-8
編碼和解碼。因此,為了最大限度地使用在異構環境中的互操作性,建議基於文本的內容使用UTF-8編碼.
Header receipt
任何除了CONNECT
的客戶端幀可以為receipt
header指定任何值。這會讓服務端應答帶有RECEIPT
的客戶端幀的處理過程。
Repeated Header Entries
Since messaging systems can be organized in store and forward topologies, similar to SMTP, a message may traverse several messaging servers before reaching a consumer. A STOMP server MAY ‘update’ header values by either prepending headers to the message or modifying a header in-place in the message.
如果client或者server受到重復的header條目,只有第一個會被用作header條目的值。其他的值僅僅用來維持狀態改變,或者被丟棄。
例如,如果client收到:
MESSAGE
foo:World
foo:Hello
^@
foo
header的值為World
.
大小限制
為了客戶端濫用服務端的內存分配,服務端可以設置可分配的內存大小:
- 單個幀允許幀頭的個數
- header中每一行的最大長度
- 幀體的大小
如果超出了這些限制,server應該向client發送一個error
frame,然后關閉連接.
連接延遲
STOMP servers必須支持client快速地連接server和斷開連接。 這意味着server在連接重置前只允許被關閉的連接短時間地延遲.
結果就是,在socket重置前client可能不會收到server發來的最后一個frame(比如ERROR
或者RECEIPT
frame去應答DISCONNECT
frame)
Connecting
STOMP client通過CONNECT
frame與server建立流或者TCP連接.
CONNECT
accept-version:1.2
host:stomp.github.org
^@
如果server收到請求,將返回CONNECTED
frame:
CONNECTED
version:1.2
^@
server能拒絕所有的連接請求。server應該響應ERROR
frame去說明為什么連接被拒絕然后關閉連接。
CONNECT or STOMP Frame
STOMP servers 處理STOMP
frame必須和處理CONNECT
frame一樣。STOMP1.2
clients應該繼續使用CONNECT
command去向后兼容1.0
.
使用STOMP
frame的clients只能連接上STOMP1.2
servers(以及一些STOMP1.1 servers),但是好處在於協議探針能夠從HTTP連接中區分開STOMP連接。
STOMP 1.2 clients 必須
設置以下headers: * accept-version
: clients支持的STOMP的版本號。詳情見Protocol_Negotiation * host
:client希望連接的虛擬主機名字,建議設置已經連接的socket為主機名,或者任何名字。如果headers沒有匹配到任何可用的虛擬主機,支持虛擬主機的servers將選擇默認的虛擬主機或者拒絕連接。
STOMP 1.2 clients可選擇
設置以下headers: * login
: 用於在server驗證的用戶id *passcode
: 用於在server驗證的密碼 * heart-beat
: 心跳設置
CONNECTED Frame
STOMP 1.2 servers 必須
設置以下headers:
version
: 會話中STOMP版本。詳情見Protocol_Negotiation
STOMP 1.2 servers可選擇
設置以下headers:
- heart-beat: 心跳設置
- session: 唯一的會話identifier
-
server: 描述STOMP server信息。它必須包含
server-name
,可以包含一些注釋信息(用空格分開)server-name
后面也可以帶着可選的版本號.server = name ["/" version] *(comment)
例如:
server:Apache/1.3.9
Protocol Negotiation
STOMP1.1 以后的版本,CONNECT
frame必須包括accept-version
header.它的值為clients支持的STOMP版本號,多個版本號用,
隔開。如果不存在accept-version
header,那么表明clients只支持1.0.
在一次會話中將使用雙方都支持的最高版本。
例如,如果client發送:
CONNECT
accept-version:1.0,1.1,2.0
host:stomp.github.org
^@
server將返回與客戶端同時支持的最高版本。
CONNECTED
version:1.1
^@
如果client和server不支持共同的協議版本,server必須返回如下的ERROR
frame,然后斷開連接。
ERROR
version:1.2,2.1
content-type:text/plain
Supported protocol versions are 1.2 2.1^@
心跳
心跳被用於去測試底層TCP連接的可用性,確保遠端服務處於活動狀態。
要使用心跳,每個部分必須聲明它能干什么以及想要其他部分干什么. 通過在CONNECT
和CONNECTED
frame中增加heart-beat
header, 讓心跳在會話開始被定義好。heart-beat
header必須包含兩個用逗號隔開的正整數。
第一個數字代表發送方能做什么: * 0
表示它不能發送心跳 * 否則它是能保證兩次心跳的最小毫秒數
第二個數字代表發送方能獲得什么: * 0
表示它不想接收心跳 * 否則它表示兩次心跳期望的毫秒數
heart-beat
header是OPTIONAL
的。沒有的話會被當作heart-beat:0,0
header 處理,意思就是說它不會發送心跳並且不想接收心跳。
heart-beat
header提供了足夠的信息去了解每個部分心跳是否可用,發送到哪里,頻率的大小.
原始frame像這個樣子:
CONNECT
heart-beat:<cx>,<cy>
CONNECTED:
heart-beat:<sx>,<sy>
對於client發送server的心跳: * 如果<cx>
為0(client不能發送心跳)或者<sy>
為0(server不想接收心跳),將不起任何作用。 * 否則心跳頻率為MAX(<cx>,<sy>)
毫秒數.
相反,<sx>
和<cy>
同樣是這樣的.
關於心跳本身,通過網絡連接收到的任何數據表明遠端服務是可用的。在給定的指向,如果心跳的頻率被期望是<n>
毫秒:
- 發送者必須每
<n>
毫秒發送新數據。 - 如果發送者沒有真實的STOMP frame,必須發送一個
end-of-line (EOL)
- 如果接受者在規定的時間內沒有收到新數據,表明連接已經斷開
- 由於時間誤差,接收者應該容錯和考慮定義錯誤的界限
Client Frames
client可以發送下列列表以外的frame,但是STOMP1.2 server會響應ERROR
frame,然后關閉連接。
SEND
SEND
frame發送消息到目的地,它必須包含表示目的地地址的destination
header.SEND
frame body是被發送的消息。例如:
SEND
destination:/queue/a
content-type:text/plain
hello queue a
^@
這個消息被發送到/queue/a
.注意STOMP把目的地看作為一個不透明的字符串,沒有目的地假設的交互語義.你應該查閱STOMP server文檔,搞清楚如何構造目的地名字。
可靠的消息語義是server指定的,依賴備用的目的地的值和其他消息headers,比如事務headers,或者其他server指定的消息headers。
SEND
可以添加transaction
header來支持事務處理.
如果body存在,那么SEND
frame應該包含一個content-length
和content-type
header
一個應用可以給SEND
frame增加任意多個用戶定義的headers。 通常用於用戶定義的頭,讓消費者能夠根據應用程序定義的報頭使用選擇訂閱幀過濾消息。 被定義的用戶必須通過MESSAGE
frame傳送。
如果server不能無故成功處理SEND
frame,那么server必須向client發送ERROR
frame然后關閉連接。
SUBSCRIBE
SUBSCRIBE
frame用於注冊給定的目的地.和SEND
frame一樣,SUBSCRIBE
frame需要包含destination
header表明client想要訂閱目的地。 被訂閱的目的地收到的任何消息將通過MESSAGE
frame發送給client。 ack
header控制着確認模式。
例子:
SUBSCRIBE
id:0
destination:/queue/foo
ack:client
^@
如果server不能成功創建此次訂閱,那么server將返回ERROR
frame然后關閉連接。
STOMP服務器可能支持額外的服務器特定的頭文件,來自定義有關訂閱傳遞語義.
SUBSCRIBE id Header
一個單連接可以對應多個開放的servers訂閱,所以必須包含id
header去唯一標示這個訂閱.這個id
frame可以把此次訂閱與接下來的MESSAGE
frame和UNSUBSCRIBE
frame聯系起來。
在相同的連接中,不同的訂閱必須擁有不同訂閱id。
SUBSCRIBE ack Header
ack
header可用的值有auto
, client
,client-individual
, 默認為auto
.
當ack
為auto
時,client收到server發來的消息后不需要回復ACK
frame.server假定消息發出去后client就已經收到。這種確認方式可以減少消息傳輸的次數.
當ack
為client
時, client必須發送ACk
frame給servers, 讓它處理消息.如果在client發送ACK
frame之前連接斷開了,那么server將假設消息沒有被處理,可能會再次發送消息給另外的客戶端。client發送的ACK
frame被當作時積累的確認。這就意味這種確認方式會去操作ACK
frame指定的消息和訂閱的所有消息
由於client不能處理某些消息,所以client應該發送NACK
frame去告訴server它不能消費這些消息。
當ack模式是client-individual
,確認工作就像客戶端確認模式(除了由客戶端發送的ACK
或NACK
幀)不會被累計。這意味着,后續ACK
, NACK
消息幀,也不能影響前面的消息的確認。
UNSUBSCRIBE
UNSUBSCRIBE
frame被用於去移除已經存在訂閱。一旦訂閱被刪除后,STOMP連接將不再會收到來自訂閱發出的消息。
一個單連接可以對應多個開放的server訂閱,所以必須包含id
header去唯一標示被刪除的訂閱.這個header中的id必須匹配已存在訂閱.
例如:
UNSUBSCRIBE
id:0
^@
ACK
ACK
用client
和client-individual
去確認訂閱消息的消費.只有通過ACK
確認過后,訂閱的消息才算是被消費.
ACK
frame必須包含一個id
header去匹配將要被確認的ack
header中的id.可以選擇地指定transaction
header表明消息確認應該是命名事務地一部分。
ACK
id:12345
transaction:tx1
^@
NACK
NACK
有ACK
相反地作用。它地作用是告訴server client不想消費這個消息。server然后發送這個消息給另外的client,丟棄它或者把它放在無效的消息隊列中。這種准確的行為是server特定的。
NACK
有相同的ACK
headers: id
(必選)和transaction
(可選)。
NACK
適用於單個消息(訂閱的ack
模式為client-individual
), 或者那些還沒有被ACK'ed
和NACK'ed
的消息(訂閱模式ack為client
).
BEGIN
BEGIN
用於事務的開始。事務被用於發送和確認消息,被發送和被確認的消息在事務過程中會被自動處理。
BEGIN
transaction:tx1
^@
transaction
header是必填的,並且事務id將被用於在SEND, COMMIT, ABORT, ACK, and NACK frames去綁定命名的事務.在相同的連接中,不同事務必須
用不同的id
如果client發送DISCONNECT
frame或者TCP連接失敗,任何已開始但沒有提交的事務默認都會被中斷.
COMMIT
COMMIT
用於在過程中提交事務.
COMMIT
transaction:tx1
^@
transaction
header是必填的並且必須
指定將要提交的事務的id.
ABORT
ABORT
用於在過程中回滾事務.
ABORT
transaction:tx1
^@
transaction
header是必填的並且必須
指定將要提交的事務的id.
DISCONNECT
client能在任何時候斷開server的連接,但是不能保證已經發送的frame已經到達了server。為了讓這一切顯得不那么暴力,client確保所有已經發送的frames已經被server收到,client應該做以下3點:
-
發送帶有
receipt
header的DISCONNECT
frameDISCONNECT receipt:77 ^@
-
等待帶有
RECEIPT
frame的響應RECEIPT receipt-id:77 ^@
-
關閉socket
注意,如果server過早地關閉socket,client將不會收到期望地RECEIPT
frame.見Connection_Lingering
client發送DISCONNECT
frame后不必要在發送任何frame.
Server Frames
server偶爾也會發送frame給客戶端(除了連接最初的CONNECTED
frame).
這些frames為: * MESSAGE * RECEIPT * ERROR
MESSAGE
MESSAGE
frame用於將訂閱的消息發送給client.
MESSAGE
frame必須包含destination
header表明信息要到達的目的地。如果消息已經用STOMP發送,那么destination
header應該和SEND
frame中的一樣。
MESSAGE
frame必須
包含帶有唯一標識的message-id
header和帶有將接收消息的訂閱的idsubscription
header.
如果從訂閱收到的消息需要明確作出確認(client
或者client-individual
模式),那么MESSAGE
frame必須
包含帶有任何值的ack
header.這個header將把消息和后來的ACK
,NACK
frame關聯起來。
下面這個frame body包含了消息的內容:
MESSAGE
subscription:0
message-id:007
destination:/queue/a
content-type:text/plain
hello queue a^@
如果frame body包含內容的話,MESSAGE
frame應該包含content-length
header和content-type
header.
除了那些server指定的headers, 消息被發送到目的地時,MESSAGE
frame同樣應該包括所有用戶定義的headers.查閱有關文檔,找出那些server指定添加到messages的headers.
RECEIPT
server成功處理請求帶有receipt
的client frame后, 將發送RECEIPT
frame到client.RECEIPT
frame必須
包含receipt-id
header,它的值為client frame中receipt
header的值。
RECEIPT
receipt-id:message-12345
^@
RECEIPT
frame是作為server處理的client frame后的應答. 既然STOMP是基於流的,那么receipt
也是對server已經收到所有的frames的累積確認。但是,以前的frames可能並沒有被完全處理。如果clients斷開連接,以前接收到的frames應該繼續被server處理。
ERROR
如果出錯的話,server將發送ERROR
frame.這種情況下,server還應該斷開連接。查看下一章connection lingering ERROR
frame應該包含帶有簡單錯誤信息的message
header,或者Body
包含詳細的描述信息,也可能沒有。
ERROR
receipt-id:message-12345
content-type:text/plain
content-length:171
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.
^@
如果錯誤關聯到了具體的某個client frame,那么server應該增加額外的headers去識別引起錯誤的frame。例如,如果frame包含receipt
header,ERROR
frame應該設置receipt-id
header的值為引起錯誤的frame的receipt
header的值。
如果frame body包含內容的話,ERROR
frame應該包含content-length
header和content-type
header
Frames and Headers
除了上述標准headers之外(content-length
, content-type
, receipt
),下面列出了所有規范中定義的headers:
- CONNECT or STOMP
- REQUIRED: accept-version, host
- OPTIONAL: login, passcode, heart-beat
- CONNECTED
- REQUIRED: version
- OPTIONAL: session, server, heart-beat
- SEND
- REQUIRED: destination
- OPTIONAL: transaction
- SUBSCRIBE
- REQUIRED: destination, id
- OPTIONAL: ack
- UNSUBSCRIBE
- REQUIRED: id
- OPTIONAL: none
- ACK or NACK
- REQUIRED: id
- OPTIONAL: transaction
- BEGIN or COMMIT or ABORT
- REQUIRED: transaction
- OPTIONAL: none
- DISCONNECT
- REQUIRED: none
- OPTIONAL: receipt
- MESSAGE
- REQUIRED: destination, message-id, subscription
- OPTIONAL: ack
- RECEIPT
- REQUIRED: receipt-id
- OPTIONAL: none
- ERROR
- REQUIRED: none
- OPTIONAL: message
除此之外,SEND
和MESSAGE
frames可能包含任意的用戶定義的headers ,它們會成為carried message的一部分。同樣,ERROR
frame應該包含額外的headers來識別引起錯誤的frame。
最終,STOMP servers可以用額外的headers去訪問持久化
或者有效期
特性.查閱server文檔獲得更多信息。
Augmented BNF
A STOMP session can be more formally described using the Backus-Naur Form (BNF) grammar used in HTTP/1.1 RFC 2616.
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 ":">
License
This specification is licensed under the Creative Commons Attribution v3.0 license.