聶永的博客

          記錄工作/學(xué)習(xí)的點點滴滴。

          MQTT 3.1協(xié)議非嚴(yán)肅反思錄

          前言

          MQTT 3.1協(xié)議在弱網(wǎng)絡(luò)環(huán)境下(比如2G/3G等)表現(xiàn)不夠好,因此才有了反思。

          弱網(wǎng)環(huán)境下表現(xiàn)

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

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

          從TCP建立開始的三次握手到完整的訂閱命令發(fā)送完畢,考慮到TCP堆棧的每次接收數(shù)據(jù)方響應(yīng)ACK,這中間終端和服務(wù)器端至少產(chǎn)生了10次數(shù)據(jù)交互。
          

          在網(wǎng)絡(luò)變化頻繁或者不太穩(wěn)定的2G/3G網(wǎng)絡(luò)環(huán)境下,這種過程顯得有些冗長和不適應(yīng),同時會加重已經(jīng)不堪的弱網(wǎng)絡(luò)負載的負擔(dān)。

          弱網(wǎng)下,在任何一個階段的執(zhí)行過程中,都有可能產(chǎn)生突發(fā)性的網(wǎng)絡(luò)中斷的問題:

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

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

          3. 發(fā)送SUBSCRIBLE命令后,但服務(wù)器端沒收到,或因為丟包,或網(wǎng)絡(luò)已斷開,導(dǎo)致發(fā)送SUBSCRIBLE命令失敗

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

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

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

          總之,針對低速率弱網(wǎng)絡(luò)環(huán)境,MQTT表現(xiàn)不怎么好。

          改進點

          業(yè)務(wù)改進點:

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

          協(xié)議改進點:

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

          有些建議看似冗余,批量或打包處理總比單個處理更高效一些、更節(jié)省資源,弱網(wǎng)絡(luò)環(huán)境要求交互要盡可能的少,數(shù)據(jù)嘛要的是瞬間抵達,越快越好。

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

          MQTT-SN

          針對沒有TCP/IP等網(wǎng)絡(luò)堆棧支持的終端環(huán)境,MQTT愛莫能助了。

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

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

          Image

          更多協(xié)議細節(jié),有待進一步閱讀。

          TCP不是最適合的移動網(wǎng)絡(luò)傳輸協(xié)議

          先來算一下網(wǎng)絡(luò)傳輸?shù)淖止?jié)數(shù)。

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

          • TCP數(shù)據(jù)包頭部信息至少占有66個字節(jié)
          • UDP數(shù)據(jù)報頭部信息至少占有54個字節(jié)

          UDP可以比TCP節(jié)省12個字節(jié)。

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

          再看看弱網(wǎng)環(huán)境。

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

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

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

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

          連接協(xié)議方面:

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

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

          小結(jié)

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

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

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

          評論

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2014-12-16 17:20 tangsir

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2014-12-17 10:25 nieyong

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2014-12-17 23:21 tangsir

          謝大神!@nieyong
            回復(fù)  更多評論   

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2014-12-18 22:06 nieyong

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

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

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

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

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

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

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

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

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-07 21:33 kdm

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-08 09:49 nieyong

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-14 21:53 kdm

          @nieyong
          請教一下根據(jù)clientId如何做,請大神指導(dǎo)  回復(fù)  更多評論   

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-15 10:04 nieyong

          @kdm
          clientId等同于topic就行  回復(fù)  更多評論   

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-15 17:45 kdm

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-05-18 16:41 nieyong

          @kdm
          是的,比如根據(jù)協(xié)議,只需要注冊一次,服務(wù)器端可持久化topic和clientId的對應(yīng)關(guān)系,后面不需要再次注冊等。或者再簡單一些,就直接根據(jù)clientId作為topic就行。

          怎么說呢,越是海量用戶/終端的系統(tǒng),協(xié)議交互層面需要越簡單,架構(gòu)層面也是如此。  回復(fù)  更多評論   

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-06-12 13:56 Justin Gao

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

          本文說了半天,就是一個問題,當(dāng)subscribe命令在交互時候出現(xiàn)問題怎么辦?

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

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

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2015-09-13 09:32 mqtt

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

          # re: MQTT 3.1協(xié)議非嚴(yán)肅反思錄 2016-05-23 07:34 Jammers

          This is very nice. Thank you.  回復(fù)  更多評論   

          公告

          所有文章皆為原創(chuàng),若轉(zhuǎn)載請標(biāo)明出處,謝謝~

          新浪微博,歡迎關(guān)注:

          導(dǎo)航

          <2014年12月>
          30123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          統(tǒng)計

          常用鏈接

          留言簿(58)

          隨筆分類(130)

          隨筆檔案(151)

          個人收藏

          最新隨筆

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 汶川县| 兰坪| 崇礼县| 安丘市| 大余县| 黄浦区| 舟山市| 芦山县| 专栏| 即墨市| 郴州市| 堆龙德庆县| 庆安县| 淮滨县| 谷城县| 黑水县| 那坡县| 天等县| 赣州市| 晋江市| 乌兰察布市| 交城县| 德兴市| 井陉县| 察隅县| 商城县| 肥东县| 新平| 乳山市| 林西县| 怀集县| 兰坪| 湘潭市| 望都县| 景德镇市| 兴仁县| 静乐县| 新泰市| 竹山县| 松潘县| 平凉市|