聶永的博客

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

          MQTT 3.1協議非嚴肅反思錄

          前言

          MQTT 3.1協議在弱網絡環境下(比如2G/3G等)表現不夠好,因此才有了反思。

          弱網環境下表現

          手機等終端在弱網絡環境下丟包情況會非常明顯,連接MQTT Server成功率很低。相比單純的請求-相應模型的HTTP,其成功率會比MQTT訂閱成功高很多。

          手機終端在每次TCP斷開或斷網后,會即刻發起TCP重連,連接成功,會重復以前步驟依次發送連接命令(CONNECT),訂閱命令(SUBSCRIBLE),表明上看,這些過程沒有任何問題,但問題就在于從終端成功建立到服務器的連接,到發送訂閱命令,在弱網情況下,這個過程將會變得很昂貴:

          從TCP建立開始的三次握手到完整的訂閱命令發送完畢,考慮到TCP堆棧的每次接收數據方響應ACK,這中間終端和服務器端至少產生了10次數據交互。
          

          在網絡變化頻繁或者不太穩定的2G/3G網絡環境下,這種過程顯得有些冗長和不適應,同時會加重已經不堪的弱網絡負載的負擔。

          弱網下,在任何一個階段的執行過程中,都有可能產生突發性的網絡中斷的問題:

          1. 無法成功建立TCP鏈接,或死在三次握手期間,或數據包丟失在握手之后,或客戶端連接超時過小

          2. 建立連接后,發送CONNECT命令后,或沒接收到TCP ACK確認包,或客戶端等待延時太小,導致訂閱命令交互失敗

          3. 發送SUBSCRIBLE命令后,但服務器端沒收到,或因為丟包,或網絡已斷開,導致發送SUBSCRIBLE命令失敗

          4. 成功發送SUBSCRIBLE命令后,或移動網絡斷開了(有些運營商針對認為HTTP的請求有超時判斷),或等待超時,導致訂閱失敗

          TCP是無感知的虛擬連接,中間斷開兩端不會立刻得到通知,否則就用不著心跳保活機制了。

          舉一個例子,線上的服務器根據日志分析,只接收到連接命令(CONNECT)但沒有后續的訂閱命令(SUBSCRIBLE)的情況,每天有上百萬級別的數量。

          總之,針對低速率弱網絡環境,MQTT表現不怎么好。

          改進點

          業務改進點:

          1. 客戶端的連接超時、等待超時設大一點,兩秒太短,可設置長一些,比如10秒
          2. 服務器端支持在接收到用戶發送CONNECT命令后,瞬間發送一些live data/hot data(早已緩存的數據),類似于HTTP請求-相應模型,目的嘛,一些熱數據發送給終端要趁早,越快越好(所謂出名要趁早嘛);這個需要客戶端、服務器端同時支持

          協議改進點:

          1. CONNECT命令可變頭部包含"MQisdp"太多余了,學院派風格嘛
          2. 允許在連接命令中負載(payload)中攜帶訂閱Topic字符串
          3. 允許在連接命令中表示上次連接訂閱的Topic發生變化否,攜帶訂閱業務,雖冗余,但實用。 eg:訂閱的Topic沒有發生變化,TOPICCHANGE:0;退訂,UNSUBSCRIBE:TOPICONE;SUBSCRIBE:TOPIC_TWO
          4. PUBLISH、PUBACK等支持的 Message Identifier 才16位,太短,實際業務無法做到全局唯一。引入mid和業務id的映射對應關系?那是狀態,需要維護,代價還是蠻高的。業界流行看法,無狀態化的架構才是便于橫向、豎向、縱向、四方向的擴展,呵呵。最好方式就是修改使之支持字符串形式,否則維護代價高!
          5. 心跳命令PINGREQ/PINGREQ可以做到一個字節傳輸,節省一個字節,有些強迫癥的感覺嘛
          6. 低速率網絡需要做一些兼容和調整

          有些建議看似冗余,批量或打包處理總比單個處理更高效一些、更節省資源,弱網絡環境要求交互要盡可能的少,數據嘛要的是瞬間抵達,越快越好。

          嚴格的分層和業務解耦,會導致性能問題。好比當前Linux內核的TCP/IP網絡堆棧分層很清晰,每一層都各司其職,但和直接略過內核態直接運行在用戶態(User Space)的Packet I/O相比,處理性能不是在一個檔次上,比如Netmap 、DPDK等。

          MQTT-SN

          針對沒有TCP/IP等網絡堆棧支持的終端環境,MQTT愛莫能助了。

          在一些類似于傳感器電子元件中,資源十分受限,計算能力不足,嵌入TCP/IP網絡堆棧不現實,比較好的方式基于IEEE 802.15.4用于低速無線個人域網(LR-WPAN)的物理層和媒體接入控制層規范之上發送UDP數據包,每一個數據包最大128個字節。

          MQTT-SN(MQTT For Sensor Networks)協議就是為了非常受限類似傳感器而設的,協議流程架構比較有趣:

          Image

          更多協議細節,有待進一步閱讀。

          TCP不是最適合的移動網絡傳輸協議

          先來算一下網絡傳輸的字節數。

          以太網幀頭至少18個字節,IP頭固定20個字節,TCP頭20個字節(UDP頭部8個字節),再加上電信寬帶計費的PPPoE的8個字節:

          • TCP數據包頭部信息至少占有66個字節
          • UDP數據報頭部信息至少占有54個字節

          UDP可以比TCP節省12個字節。

          MQTT-SN協議選擇使用UDP,可以看出其在節省資源方面的努力。

          再看看弱網環境。

          • 在網絡可達情況下,UDP可以在TCP建立第一次握手期間就已經把數據送達目的地
          • 完成三次握手期間,UDP客戶端和UDP服務器在數據層面可以完成一次完整的交互(PING-PONG)

          在網絡不好的情況下,UDP的時效性會好于TCP,TCP長連接中間交換過多、使之建立完整交互的過程成功率就很低。此種情況UDP的低延遲和實時性呈現的結果會表現的很突出。

          TCP或HTTP理論上是可靠連接,但是在網絡不好的時候,也不是那么可靠。客戶端一般提交HTTP請求之后,沒有確認是否提交成功,在弱網環境下會產生丟包,服務器端嘛收不到。另TCP網絡堆棧會存在數據包重發機制 + 應用層重發請求,可能會導致內核處理多次數據包的重發(還有擁塞窗口會收縮,發包速度減慢),可能會加重弱網絡的負載。

          和TCP相比,UDP的無連接,代表了它快速,資源消耗小,突出表現就是延遲較小。至于數據包丟失沒有重傳,上層的業務層面應用協議/機制可以確保丟失的數據包重發或補發等,并且會更透明,安全的控性權。而TCP的包重發,上層應用沒有控制權限。

          連接協議方面:

          • TCP面向連接會產生狀態管理和維護,成本不小,比如經常看到的客戶端reset異常等。一次完整的請求周期必須固定在一臺服務器上
          • UDP無連接的特性。每次請求的數據包可以隨機分配到不同的機器上進行處理,可以做到完全無狀態化橫向擴展

          總之,要實時性特診,或者快速抵達終端的特性,不妨考慮一下UDP。不過呢,很多時候UDP和TCP大家會混合著使用,會互相彌補其不足。

          小結

          若MQTT協議不能夠滿足業務需求,或許可考慮選擇定制,或簡化流程,或使用UDP重新實現,或者使用TCP/HTTP作為補充等,不一而足。

          想想,還真有點小激動呢! ------ 《萬萬沒想到》王大錘

          posted on 2014-12-12 10:19 nieyong 閱讀(31299) 評論(17)  編輯  收藏 所屬分類: MQTT

          評論

          # re: MQTT 3.1協議非嚴肅反思錄 2014-12-16 17:20 tangsir

          你好,謝謝你的文章。
          我們有個手機APP,目前的方案是實時數據直接socket連接(不好的地方是客戶端服務端都要寫代碼),想全面轉向mqtt(這樣服務端就專注于從mqtt對立里面獲取數據,進行業務處理,并發送消息即可),邏輯上應該更清楚,代碼開發也方便。但是聽你這個介紹有點猶豫了,要不要改呢?請聽你的指教!  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2014-12-17 10:25 nieyong

          @tangsir
          目前除了MQTT,暫時找不到比它更好的業界標準了。建議選擇MQTT 3.1.1協議:
          支持客戶端在發送完畢CONNECT之后,無須等待服務器響應直接發送其余命令
          支持服務器和客戶端兩端暫時會話保存,上次連接之后,再次CONNECT,會話標志位true,可無須發送SUBSCRIBLE命令
          具體請參考:
          MQTT 3.1.1,值得升級的6個新特性  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2014-12-17 23:21 tangsir

          謝大神!@nieyong
            回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2014-12-18 22:06 nieyong

          @tangsir
          措辭有問題,可不是什么大神,呵呵。
          只是總結而已。  回復  更多評論   

          # Bad Piggies Game 2014-12-19 15:55 Bad Piggies Game

          措辭有問題,可不是什么大神,呵呵。  回復  更多評論   

          # Jogos Frin 2014-12-22 11:55 friv4school2015@hotmail.com

          措辭有問題,可不是什么大神,呵呵。  回復  更多評論   

          # Jogos Frin 2014-12-22 11:55 friv4school2015@hotmail.com

          措辭有問題,可不是什么大神,呵呵。   回復  更多評論   

          # Friv School 2015 2014-12-25 11:07 yepimatasa

          措辭有問題,可不是什么大神,呵呵  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-07 21:33 kdm

          你好我們有手機設備也有硬件設備,不同的設備需要根據不同的情況推送各自的數據,如果我們有10萬個終端,我們需要建立10萬個topic嗎?有沒有其它地方法謝謝?  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-08 09:49 nieyong

          @kdm
          其實,靈活一點是不需要建立10W個topic的,根據clientId就可以  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-14 21:53 kdm

          @nieyong
          請教一下根據clientId如何做,請大神指導  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-15 10:04 nieyong

          @kdm
          clientId等同于topic就行  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-15 17:45 kdm

          @nieyong
          那還是每個設備一個topic對吧。topic的主題為clientId.  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-05-18 16:41 nieyong

          @kdm
          是的,比如根據協議,只需要注冊一次,服務器端可持久化topic和clientId的對應關系,后面不需要再次注冊等。或者再簡單一些,就直接根據clientId作為topic就行。

          怎么說呢,越是海量用戶/終端的系統,協議交互層面需要越簡單,架構層面也是如此。  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-06-12 13:56 Justin Gao

          看了這篇文章,實話說有點不以為然

          本文說了半天,就是一個問題,當subscribe命令在交互時候出現問題怎么辦?

          回到協議本身,事實上是很簡單的一個答案,只有收到正確的suback命令才能認為subscribe成功,否則客戶端有義務不斷地重復發送subscribe。

          而判斷正確返回否則管理著重發的機制本來就是message queue的精髓所在,在客戶端維護一個最基礎記錄subscribe的queue,原始意義上并不是特別難。

          至于本文所提的用UDP協議,或者和改造協議部分,重傳部分數據的方法,只會使協議復雜度變高很多,出錯可能性增加;在高并發的環境中,并不可取。而相對來說,客戶端做更改難度小很多。  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2015-09-13 09:32 mqtt

          subscribe讓服務器自動訂閱,把客戶端subscribe功能禁止掉  回復  更多評論   

          # re: MQTT 3.1協議非嚴肅反思錄 2016-05-23 07:34 Jammers

          This is very nice. Thank you.  回復  更多評論   

          公告

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

          新浪微博,歡迎關注:

          導航

          <2015年5月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          統計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 普安县| 巴东县| 龙陵县| 清镇市| 新津县| 德化县| 哈巴河县| 水城县| 苗栗县| 南雄市| 小金县| 和田市| 延安市| 新源县| 壶关县| 东辽县| 秦皇岛市| 连江县| 五原县| 海门市| 阳曲县| 长阳| 左贡县| 邢台县| 来宾市| 武穴市| 土默特左旗| 南城县| 台北市| 韩城市| 卫辉市| 航空| 平南县| 峨山| 海宁市| 陆河县| 连山| 和平区| 枣庄市| 柳江县| 屯门区|