2014年2月27日

MQTT(五)SUBSCRIBE Message

前言:

上一篇提到用來發佈訊息的PUBLISH Message,接著就來談談訂閱訊息用的SUBSCRIBE Message。

SUBSCRIBE Message

SUBSCRIBE Message允許client對Server上一或多個感興趣的主題(Topic)進行訂閱。當有訊息對主題發佈時,server會將訊息當作PUBLISH Message送給訂閱的client。SUBSCRIBE Message也定義了QoS level,表示它期望以何種QoS level來接收訊息。

Fixed Header:

bit 7 6 5 4 3 2 1 0
byte 1 Message Type(8) DUP flag QoS level RETAIN
1 0 0 0 0 0 1 x
byte 2 Remaining Length

上面為SUBSCRIBE Message裡面的fixed header,以下依依介紹它們的作用。

  • Message Type:每種類型的Message都要用到,要根據想用的訊息類型填上相對應的值,值可以參考先前貼的文章MQTT(二)Message Type and Flows。這邊由於是SUBSCRIBE Message的關係,因此會填上1000。

  • DUP flag:標記此訊息為重複(duplicate)的訊息,會用在PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE上。當client或是server要重新傳送上述訊息時,則要將此flag設為1。此flag會適用於QoS大於0的訊息。

    此flag 只能當成一種提示,表示此則訊息可能在之前有收過了,不應該拿它來當成訊息重複的檢查flag。

  • QoS level:SUBSCRIBE Message使用QoS level 1來回應(acknowledge)不同的訂閱主題請求。

  • RETAIN:沒有使用到。

  • Remaining Length:此欄位記錄當前的Message,它的Variable header和Payload總共的長度為何,此欄位不一定就是1 byte,它是可變得,最多到4 byte。

接著讓我們繼續往下看,再來是第二個Herader,也就是Variable Header。

Variable Header:

由於SUBSCRIBE Message QoS level為1,因此其variable header會包含Message ID,用來確保訊息能夠正確傳送。

下面為SUBSCRIBE Message,variable header欄位範例:

Field Value
Message ID 10

Payload:

SUBSCRIBE Message的payload會包含一個主題名稱列表,代表該Client想要訂閱的主題,列表內的每個主題都會包括一個期望接收訊息時的QoS level,可以參考MQTT(二)Message Type and Flows:Subscribe Flow來了解QoS level與其影響為何。

主題的名稱可以使用萬用字元(wildcard characters),關於萬用字元稍後會進行說明,以下為SUBSCRIBE Message的payload範例:

Topic name "a/b"
Requested QoS 1
Topic name "c/d"
Requested QoS 2

上述範例代表該Client想要訂閱兩個主題"a/b"、"c/d",其中主題"a/b"期望用QoS level 1來接收訊息,主題"c/d"期望用QoS level 2來接收訊息。

Response:

當Server接收到從Cleint發送過來的SUBSCRIBE Message,則回應一個SUBACK Message給client。

Server有基於Client已經訂閱的關係,因此可能會開始傳送PUBLISH Message給Client,即使Client還沒收到SUBACK Message。

Server有可能會降低cleint請求的QoS level。這會發生在Server沒辦法提供高level的QoS時。舉例來說,如果Server沒有提供一個可靠的訊息持久機制(persistence mechanism),則它可能只提供QoS level 0給訂閱者。Server提供的QoS level會在SUBACK Message的payload內,依照主題的訂閱順序回應給訂閱者。以下為範例:

Granted QoS 0
Granted QoS 2

主題萬用字元(Topic WildCard Characters):

在訂閱的時候可以使用特出字元,允許你一次訂閱多個主題,此字元稱為萬用字元。

主題層級分隔符號(Topic level separator)被用來對主題做結構化。單一層級的萬用字元和多層級的萬用字元可以被用在訂閱上面,但是不能被用在發佈(publish)訊息上面,也就是說在發佈訊息時,主題要明確定義,不能用萬用字元來同時對多個主題做訊息發佈。

Topic level separator(主題層級分隔符號):

斜線符號(/)用來切割topic tree的每個層級,提供一個有層次的主題空間。使用主題層級分割符號來切割主題,在遇到用萬用字元訂閱該主題時是很有用的,下面接著介紹兩個萬用字元。

Multi-level wildcard(多層級萬用字元):

井字號(#)用來匹配任何層級的主題。舉例來說,如果你用此萬用字元訂閱了 finance/stock/ibm/#,則你將會收到來自於下列主題的訊息:

  • finance/stock/ibm
  • finance/stock/ibm/closingprice
  • finance/stock/ibm/currentprice

多層級萬用字元可以表示零到多個層級,因此 finance/# 可以匹配到 finance

多層級萬用字元在以下狀況是有效的,如 # 或是 finance/# ,而以下狀況下是無效的,如 finance#finance/#/closingprice

Single-level wildcard(單一層級萬用字元):

加字號(+)用來匹配單一層級的主題。舉例來說 finance/stock/+ 會匹配 /finance/stock/ibm/finance/stock/xyz ,但是不會匹配到 finance/stock/ibm/closingprice 。也由於它只會匹配到一個層級,因此 finance/+ 不會匹配到 finance

單一層級萬用字元可以使用在任何層級的主題樹,也可以和多層級萬用字元結合使用。

單一層級萬用字元在以下狀況是有效的,如 +finance/+ 以及 finance/+/ibm 。而以下狀況是無效的,如 finance+

小結

本篇介紹了MQTT的SUBSCRIBE Message,此訊息用於訂閱主題上,而每個想訂閱的主題需要設定期望的QoS level,藉此來表達client對該主題的訊息品質需求。

另外也介紹了主題萬用字元,讓client在訂閱主題時,可以透過萬用字元同時對多個主題進行訂閱。

參考:

MQTT V3.1 Protocol Specification