聶永的博客

          記錄工作/學習的點點滴滴。

          MQTT協議筆記之消息流

          前言

          前面的筆記已把所有消息類型都過了一遍,這里從消息流的角度嘗試解讀一下。

          網絡故障

          在任何網絡環境下,都會出現一方連接失敗,比如離開公司大門那一刻沒有了WIFI信號。但持續連接的另一端-服務器可能不能立即知道對方已斷開。類似網絡異常情況,都有可能在消息發送的過程中出現,消息發送出去,就丟失了。

          MQTT協議假定客戶端和服務器端穩定情況一般,彼此之通信管道不可靠,一旦客戶端網絡斷開,情況就會很嚴重,很難恢復原狀。

          但別忘記,很多客戶端會有永久性存儲設備支持,比如閃存ROM、存儲卡等,在通信出現異常的情況下可以用于保存關鍵數據或狀態信息等。

          總之,異常網絡情況很復雜,只能小心處理之。

          消息重發策略

          QoS > 0情況下,PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE等類型消息在發送者發送完之后,需要等待一個響應消息,若在一個指定時間段內沒有收到,發送者可能需要重試。重發的消息,要求DUP標記要設置為1.

          等待響應的超時應該在消息成功發送之后開始算起,并且等待超時應該是可以配置選項,以便在下一次重試的時候,適當加大。比如第一次重試超時10秒,下一次可能為20秒,再一次重試可能為60秒呢。當然,還要有一個重試次數限制的。

          還有一種情況,客戶端重新連接,但未在可變頭部中設置clean session標記,但雙方(客戶端和服務器端)都應該重試先前未發送的動態消息(in-flight messages)。客戶端不被強制要求發送未被確認的消息,但服務器端就得需要重發那些未被去確認的消息。

          QoS level決定的消息流

          QoS level為Quality of Service level的縮寫,翻譯成中文,服務質量等級。

          MQTT 3.1協議在"4.1 Quality of Service levels and flows"章節中,僅僅討論了客戶端到服務器的發布流程,不太完整。因為決定消息到達率,能夠提升發送質量的,應該是服務器發布PUBLISH消息到訂閱者這一消息流方向。

          QoS level 0

          至多發送一次,發送即丟棄。沒有確認消息,也不知道對方是否收到。

          Client Message and direction Server
          QoS = 0 PUBLISH
          ---------->
          Action: Publish message to subscribers then Forget
          Reception: <=1

          針對的消息不重要,丟失也無所謂。

          網絡層面,傳輸壓力小。

          QoS level 1

          所有QoS level 1都要在可變頭部中附加一個16位的消息ID。

          SUBSCRIBE和UNSUBSCRIBE消息使用QoS level 1。

          針對消息的發布,Qos level 1,意味著消息至少被傳輸一次。

          發送者若在一段時間內接收不到PUBACK消息,發送者需要打開DUB標記為1,然后重新發送PUBLISH消息。因此會導致接收方可能會收到兩次PUBLISH消息。針對客戶端發布消息到服務器的消息流:

          Client Message and direction Server
          QoS = 1
          DUP = 0
          Message ID = x

          Action: Store message

          PUBLISH
          ---------->
          Actions:
          • Store message

          • Publish message to subscribers
          • Delete message

          Reception: >=1
          Action: Discard message PUBACK
          <----------
          Message ID = x

          針對服務器發布到訂閱者的消息流:

          Server Message and direction Subscriber
          QoS = 1
          DUP = 0
          Message ID = x
          PUBLISH
          ---------->
          Actions:
          • Store message

          • Make message available                       
          Reception: >=1

          PUBACK
          <----------
          Message ID = x

          發布者(客戶端/服務器)若因種種異常接收不到PUBACK消息,會再次重新發送PUBLISH消息,同時設置DUP標記為1。接收者以服務器為例,這可能會導致服務器收到重復消息,按照流程,broker(服務器)發布消息到訂閱者(會導致訂閱者接收到重復消息),然后發送一條PUBACK確認消息到發布者。

          在業務層面,或許可以彌補MQTT協議的不足之處:重試的消息ID一定要一致接收方一定判斷當前接收的消息ID是否已經接受過

          但一樣不能夠完全確保,消息一定到達了。

          QoS level 2

          僅僅在PUBLISH類型消息中出現,要求在可變頭部中要附加消息ID。

          級別高,通信壓力稍大些,但確保了僅僅傳輸接收一次。

          先看協議中流程圖,Client -> Server方向,會有一個總體印象:

          Client Message and direction Server
          QoS = 2
          DUP = 0
          Message ID = x

          Action: Store message

          PUBLISH
          ---------->
          Action(a) Store message

          or

          Actions(b):
          • Store message ID
          • Publish message to subscribers
            PUBREC
          <----------
          Message ID = x
          Message ID = x PUBREL
          ---------->
          Actions(a):
          • Publish message to subscribers
          • Delete message

          or

          Action(b): Delete message ID
          Action: Discard message PUBCOMP
          <----------
          Message ID = x

          Server -> Subscriber

          Server Message and direction Subscriber
          QoS = 2
          DUP = 0
          Message ID = x
          PUBLISH
          ---------->
          Action: Store message
            PUBREC
          <----------
          Message ID = x
          Message ID = x PUBREL
          ---------->
          Actions:
          • Make message available                       

          PUBCOMP
          <----------
          Message ID = x

          Server端采取的方案a和b,都包含了何時消息有效,何時處理消息。兩個方案二選一,Server端自己決定。但無論死采取哪一種方式,都是在QoS level 2協議范疇下,不受影響。若一方沒有接收到對應的確認消息,會從最近一次需要確認的消息重試,以便整個(QoS level 2)流程打通。

          消息順序

          消息順序會受許多因素的影響,但對于服務器程序,必須保證消息傳遞流程的每個階段要和開始的順序一致。例如,在QoS level 2定義的消息流中,PUBREL流必須和PUBLISH流具有相同的順序發送:

          Client Message and direction Server
            PUBLISH 1
          ---------->
          PUBLISH 2
          ---------->
          PUBLISH 3
          ---------->
           
            PUBREC 1
          <----------
          PUBREC 2
          <----------
           
            PUBREL 1
          ---------->
           
            PUBREC 3
          <----------
           
            PUBREL 2
          ---------->
           
            PUBCOMP 1
          <----------
           
            PUBREL 3
          ---------->
           
            PUBCOMP 2
          <----------
          PUBCOMP 3
          <----------
           

          流動消息(in-flight messages)數量允許有一個可保證的效果:

          • 在流動消息(in-flight)窗口1中,每個傳遞流在下一個流開始之前完成。這保證消息以提交的順序傳遞
          • 在流動消息(in-flight)大于1的窗口,只能在QoS level內被保證消息的順序

          消息的持久化

          在MQTT協議中,PUBLISH消息固定頭部RETAIN標記,只有為1才要求服務器需要持久保存此消息,除非新的PUBLISH覆蓋。

          對于持久的、最新一條PUBLISH消息,服務器不但要發送給當前的訂閱者,并且新的訂閱者(new subscriber,同樣需要訂閱了此消息對應的Topic name)會馬上得到推送。

          Tip:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送,不是所有。
          

          消息流的編碼/解碼

          MQTT協議中,由目前定義的14種類型消息在客戶端和服務器端之間數據進行交互。若以JAVA語言構建MQTT服務器,可選擇Netty作為基礎。

          在Netty中,數據的進入和流出,代表了一次完整的交互。無論是要進入的還是要流出的數據(單獨以服務器為例),都可看做字節流。若把每種類型消息抽象為一個具體對象,那么處理起來就不難了。

          客戶端->服務器,進入的字節流,逐個字節/單位讀取,可還原成一個具體的消息對象(解碼的過程)。

          要發送到客戶端的消息對象,轉換(編碼)成字節流,然后由TCP通道流轉到接收者。

          小結

          斷斷續續記錄了MQTT 3.1協議的若干閱讀筆記,總之是把協議個人認為不夠清晰,或者我不好理解的地方,著重進行了分析。也便于自己以后回過來頭來翻閱,不是那么快的忘卻。

          posted on 2014-02-15 19:17 nieyong 閱讀(41521) 評論(9)  編輯  收藏 所屬分類: MQTT

          評論

          # re: MQTT協議筆記之消息流 2014-02-16 08:36 零柒鎖業

          期待更新啊  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-02-17 10:19 零柒鎖業

          支持博主分享  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-02-18 10:07 零柒鎖業

          期待更新啊  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-02-18 16:29 垂直綠化

          學習了。  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-02-28 14:59 依然清風

          最好是能有源碼的體現
          現在主要困惑在主題那塊
          比如說A,B是兩個client,A 要接受B的消息,是不是應該這樣,B發布一個主題,并且要還有A感興趣的消息,那么B發布到S(broke),然后A 要向S訂閱吧,等B的此類消息一到,就轉發給A是不是這個流程?  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-03-06 16:23 nieyong

          @依然清風
          要非常清楚總體協議,代碼不過是在實現協議。
          Client A與Client B,在邏輯上不存在轉發。
          1. Client A在Broker上訂閱/注冊Topic M
          2. Broker接收包含Topic M的消息,檢索所有訂閱Topic M的客戶端
          3. Broker逐一會發送給所有訂閱Topic M訂閱者/客戶端,包括Client A  回復  更多評論   

          # re: MQTT協議筆記之消息流 2014-03-19 11:25 zyn

          分析的很好啊,之前看了個博客,看的頭暈暈的。看了你的表格描述,慢清楚的 謝謝  回復  更多評論   

          # re: MQTT協議筆記之消息流 2015-02-27 14:41 MR.zhang

          如果設置了消息的持久化,那么那些在緩存中的topic什么時間清楚,能控制不能,會不會因為緩存太多而占用大量的內存  回復  更多評論   

          # re: MQTT協議筆記之消息流 2016-04-22 16:43 讓他一人

          瑩爾特瑞特人問題而天然  回復  更多評論   

          公告

          所有文章皆為原創,若轉載請標明出處,謝謝~

          新浪微博,歡迎關注:

          導航

          <2014年3月>
          2324252627281
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 成武县| 黔江区| 大洼县| 延寿县| 正镶白旗| 晋城| 铜山县| 乐昌市| 平武县| 邻水| 休宁县| 淮北市| 东乌珠穆沁旗| 灌阳县| 古田县| 海安县| 新绛县| 长寿区| 衡南县| 陵水| 青州市| 海安县| 古浪县| 鹰潭市| 广汉市| 馆陶县| 庆云县| 张掖市| 循化| 黔江区| 横山县| 景谷| 河西区| 宁城县| 缙云县| 扬州市| 友谊县| 察哈| 遵义县| 宁国市| 万全县|