2014年3月16日 星期日

MQTT(二)Message Type and Flows

Message Type

在MQTT協定裡,當Client要連上MQTT Server時,需要發送一個type為"CONNECT"的訊息到MQTT Server,才能開始建立起連線;當Client想發佈訊息時,則要發送一個type為"PUBLISH"的message出去;而當Client要訂閱某個Topic時,需要發送一個type為"SUBSCRIBE"的message出去。MQTT協定需要依照這些不同的type message,來完成訊息傳遞工作的。
下面就先列出,當前版本3.1所有的Message Type:
名稱代表值說明
Reserved0保留之後使用
CONNECT1Client對Server發出連線請求
CONNACK2連線請求的Ack
PUBLISH3發佈訊息
PUBACK4發佈訊息Ack(QoS level 1使用)
PUBREC5已收到發佈訊息(QoS level 2使用)
PUBREL6釋放發佈訊息(QoS level 2使用)
PUBCOMP7訊息發佈完成(QoS level 2使用)
SUBSCRIBE8Client請求訂閱
SUBACK9請求訂閱的Ack
UNSUBSCRIBE10Client取消訂閱
UNSUBACK11取消訂閱的Ack
PINGREQ12PING Request
PINGRESP13PING Response
DISCONNECT14Client斷線
Reserved15保留之後使用
光從Message Type的名稱,其實就可以猜出其中一些訊息的應用。不過如果能搭配整個流程來看,會比較清楚知道在各種場景下會使用哪些訊息。

Flows

下面根據幾個應用場景,說明訊息傳遞的順序與方向。

1.CONNECT

當client對server建立起TCP/IP socket連線時,一個protocol level session會根據CONNECT flow被建立。
ClientMessage and directionServer
CONNECT
----------->
Action: According set flag to create session.
CONNACK
<-----------
如果發送的CONNECT message為不正確的格式,則server會直接關閉連線。

2.DISCONNECT

client會對server發送DISCONNECT message,表示它要關閉TCP/IP連線。如果當初client在連線時有設定clean session flag,則所有跟此client相關的資訊都會被清除。
ClientMessage and directionServer
DISCONNECT
--------------->
Action: Clean user infomation if clean session set.

3.PUBLISH

publish會因為所選的QoS而有以下三種flow:

a. QoS level 0: At most once delivery

訊息會根據底層的TCP/IP網路盡最大努力傳送。並且沒有預期會有任何的response,也沒有定義任何訊息重新傳送的語義。訊息要嘛送到server一次,要嘛什麼都沒送達。
下表顯示QoS level 0 protocol的flow:
ClientMessage and directionServer
QoS = 0PUBLISH
----------->
Action: Publish message to subscribers

b. QoS level 1: At least once delivery

server會把PUBACK message當作ack回傳。如果在通訊連線斷線或是裝置出現錯誤,或是client沒有在一個特定時間之內收到server發送的ack message,則client要重發message並且設定message header的DUB bit。訊息至少會到達server一次。
QoS level 1會在message header內包含Message ID。
下表顯示QoS level 1 的流程:
ClientMessage and directionServer
QoS = 1
DUP = 0
Message ID = x

Action: Store message
PUBLISH
---------->
Action:
  • Store message
  • Publish message to subscribers
  • Delete message
Action:Discard messagePUBACK
<----------
當server重複收到client發送來的訊息,server一樣會把訊息publish給所有的subscriber,然後發送另一個PUBACK message回給client。

c. QoS level 2: Exactly once delivery

對上面描述的QoS level 1 protocol額外增加流程來確保重複發送的訊息不會被發送到接收訊息的application。這是最高level的發送,應用在當重複的message不被允許時的狀況(如金流資料)。它可能會增加網路流量,但因為訊息內容很重要,因此這通常是會被接受的。
QoS level 2的訊息會在message header有一個Message ID。
下表顯示QoS level 2的flow。其中在Server Side有兩個語義可以讓一個收件人決定應該要如何處理PUBLISH的流程。它們只差在訊息何時會被發佈給subscriber而已。選擇的語義不會影響到QoS level 2流程訊息只會收到一次的保證。
ClientMessage and directionServer
QoS = 2
DUP = 0
Message ID = x

Action: Store message
PUBLISH
---------->
Action: Store message

or

Actions:
  • Store message
  • Publish message to subscribers
PUBREC
<----------
Message ID = x
Message ID = xPUBREL
---------->
Actions:
  • Publish message to subscribers
  • Delete message
or

Action: Delete message ID
Action:Discard messagePUBCOMP
<----------
Message ID = x
如果有錯誤發生或是timeout了,則protocol的flow會從最後沒有ack的message開始嘗試,也就是說要嘛重新發送PUBLISH message,要嘛重新發送PUBREL message。QoS level 2這些額外的Protocol flows確保訊息只會被送到訂閱者一次。

4.SUBSCRIBE

SUBSCRIBE message允許client對server上有興趣的一個或多個topic進行註冊。如果有訊息被publish到了某個topic內,則server會把訊息PUBLISH到註冊到此topic的client。
client可以針對每個想要subscribe的topic,分別調整其QoS,舉例來說當前client subscribe兩個topic,分別為"a/b" topic 和 "c/d" topic,則client可以決定"a/b"要以QoS 0來接收,而"c/d"要以QoS 1來接收。
當然client會收到的真正QoS level,還是要依據原本PUBLISH message來決定,有可能會小於或等於client設定的。舉例來說,今天一個client對"a/b"這個topic做了QoS 1的subscribe。則:
  • QoS level 0 PUBLISH message會根據QoS 0來發送此訊息給此client,
  • QoS level 2 PUBLISH message會根據QoS 1來發送此訊息給此client。
也就是說publisher有責任決定此則訊息的最大QoS level為何,但是每個subscriber也可以依照自己的使用狀況來決定適合的QoS level。
ClientMessage and directionServer
SUBSCRIBE
----------->
SUBACK
<-----------

5.PING

一個連線中的client,可以發送PINGREQ message,詢問server是否還活著。之後server會回應一個PINGRESP message,告訴client它還活著。
ClientMessage and directionServer
PINGREQ
----------->
Action: Publish message to
PINGRESP
<-----------
Action: Publish message to

小結

本篇介紹了MQTT的各種Message Type,並且由Flow來觀察每種Message會應用在何種場景,藉此加深印象。

參考:

MQTT(一)簡介

前言

會知道MQTT協定,要先回憶起兩年前,當初想找找Android的Push Notification的解決方案,先是找到了當時GCM的前身C2DM,以為Google已經提供了此服務,測試了一下code也蠻容易上手的,結果發現到他有quota限制,不適合拿來當成產品,因此就放棄它,改找別的解決方案,最後找到了兩種不同的實作方式,一種透過XMPP協定來完成,另一種也就是今天要提到的,透過MQTT來完成。

所以MQTT是什麼?

MQTT的全名為 Message Queuing Telemetry Transport,為IBM和Eurotech共同製定出來的protocol,在MQTT的官網可以看到一開始它對MQTT的介紹:
MQTT is a machine-to-machine (M2M)/"Internet of Things" connectivity protocol. It was designed as an extremely lightweight publish/subscribe messaging transport.
簡單來說,它是為了物聯網而設計的protocol,並且它是透過publish/subscribe的方式來做訊息傳送。由於是為了物聯網而設計的協定,因此它所需要的網路頻寬是很低的,而所需要的硬體資源也是低的。

Publish/Subscribe:

在看MQTT之前,最好要先知道Publish/Subscribe的訊息傳送機制為何,這樣之後在看其協定時,才會更快上手。Publish/Subscribe有三種主要的組成元件,分別為Publisher、Subscriber以及Topic。
Publisher為訊息的來源,它會將訊息發送給Topic,而Subscriber向Topic註冊,表示他們想要接收此Topic的訊息;因此當有某個Publisher對Topic發送訊息時,只要是有對此Topic註冊的Subscriber,都會收到此則訊息。
它們的關係如下圖:

MQTT特性:

了解了Publish/Subscribe的機制之後,讓我們來看看MQTT有哪些特性:
  1. Publish/Subscribe的訊息傳送模式,來提供一對多的訊息分配。
  2. A messaging transport that is agnostic to the content of the payload.
  3. 使用TCP/IP來提供基本的網路連結。
  4. 三種訊息傳送服務的qualities:
    • "At most once",最多一次,訊息遺失或是重複發送的狀況可能會發生;這種quality適合應用在環境感測,不在意資料是否會遺失,因為下一次的資料取樣很快就會被published出來。
    • "At least once",至少一次,這種quality保證訊息會送達,只是可能會發生重複發送訊息的狀況。
    • "Exactly once",確定一次,確認訊息只會送到一次。這種quality適合用在計費系統,系統只要有重複收到資料、或是資料遺失狀況發生,就會造成系統錯誤。
  5. 由於他的header固定長度為2byte,因此可以減少封包傳送時的額外負載,並減少所需的網路頻寬。
  6. 當異常斷線發生時,會使用最後遺囑(Last Will and Testament)的機制,通知各個感興趣的client。

MQTT現況:

MQTT現階段並不是一個標準化的Protocol,還在持續改進中,目前為MQTT V3.1。不過IBM已於2013年已經將它交給OASIS進行標準化了,並且一直以來IBM對此協定採開放、免授權費的方式讓它能夠被散佈,因此相信不久的將來會成為一個主流的Protocol。
而目前支援MQTT的Client API,有Eclipse Phno Project有對MQTT client支援,其支援C、Java、Javascript、C++等等的語言,可說是支援度很高的Project。而目已經在應用MQTT的,最知名的應該就是Facebook Message App了吧,可以參考此篇文章。

小結:

上面提到的,低頻寬、低硬體需求的特性,訊息傳遞為Publish/Subscribe的方式,正好可以用來實現Push Notification的機制,並且能達到手持裝置省電的需求,接下來會先從其Protocol開始了解,並用Client Api跑些範例來應用此Protocol。

參考: