爲啥IoT(物聯網)選擇了MQTT協議?

物聯網設備要實現互相通信,須一套標準通信協議,MQTT(Message Queuing Telemetry Transport)專爲物聯網設備設計的一套標準消息隊列通信協議。使用MQTT協議的IoT設備,可以連接到任何支持MQTT協議的消息隊列上,進行通信。

  • 宏觀,MQTT和其他MQ傳輸協議差不多。也是“發佈-訂閱”消息模型
  • 網絡結構,也是C/S架構,IoT設備是客戶端,Broker是服務端,客戶端與Broker通信進行收發消息

但畢竟使用場景不同,所以,MQTT和普通MQ比,還有很多區別。

1 客戶端都運行在IoT設備

1.1 IoT設備特點

① 便宜

最大特點,一個水杯才幾十塊錢,它上面智能模塊成本十塊錢最多,再貴就賣不出去。十塊錢的智能設備內存都是按KB計算,可能都沒有CPU,也不一定有os,整個設備就一個SoC(System on a Chip)。這樣的設備就需要通信協議不能複雜,功能不能太多。

② 無線連接

IoT設備一般採用無線連接,很多設備經常移動,導致IoT設備網絡連接不穩定,且是常態。

MQTT協議設計上充分考慮這些特點。協議的報文設計極簡,惜字如金。協議功能也非常簡單,基本就只有:

  • 發佈訂閱主題
  • 收發消息

這兩個最核心功能。爲應對網絡連接不穩定問題,MQTT增加機制:

  • 心跳機制,可讓客戶端和服務端雙方都能隨時掌握當前連接狀態,一旦發現連接中斷,可儘快重連
  • 會話機制,在服務端來保存會話狀態,客戶端重連後就可恢復之前會話,繼續收發消息。這樣,把複雜度轉移到服務端,客戶端實現更簡單

2 服務端高要求

MQTT面臨的使用場景中,服務端需支撐海量IoT設備同時在線。

普通的消息隊列集羣,服務的客戶端都運行在性能強大的服務器,所以客戶端數量不會特別多。如京東的JMQ集羣,日常在線客戶端數量大概十萬左右,就足夠支撐全國人民在京東買買買。

而MQTT使用場景中,需支撐的客戶端數量,遠不止幾萬幾十萬。如北京交通委若要把全市車輛都接入進來,就是個幾百萬客戶端的規模。路側的攝像頭,每家每戶的電視、冰箱,每個人隨身攜帶的各種穿戴設備,這些設備規模都是百萬、千萬級甚至上億級。

3 不支持點對點通信

MQTT協議的設計目標是支持發佈-訂閱(Publish-Subscribe)模型,而不是點對點通信。

MQTT的主要特點之一是支持發佈者(Publisher)將消息發佈到一個主題(Topic),而訂閱者(Subscriber)則可以通過訂閱相關主題來接收這些消息。這種模型在大規模的分佈式系統中具有很好的可擴展性和靈活性。因此,MQTT更適合用於多對多、多對一的通信場景,例如物聯網(IoT)應用、消息中間件等。

雖然MQTT的設計目標不是點對點通信,但在實際使用中,你仍然可以通過一些設計來模擬點對點通信。例如,使用不同的主題來模擬點對點通信,或者在應用層進行一些額外的協議和邏輯以實現點對點通信的效果。

一般做法都是,每個客戶端都創建一個以自己ID爲名字的主題,然後客戶端來訂閱自己的專屬主題,用於接收專門發給這個客戶端的消息。即MQTT集羣中,主題數量和客戶端數量基本是同一量級。

4 MQTT產品選型

如何支持海量在線IoT設備和海量主題,是每個支持MQTT協議的MQ面臨最大挑戰。也是做MQTT服務端技術選型時,需重點考察技術點。

開源MQTT產品

有些是傳統MQ,通過官方或非官方擴展,實現MQTT協議支持。也有一些專門的MQTT Server產品,這些MQTT Server在協議支持層面,大多沒問題,性能和穩定性方面也都滿足要求。但還沒發現能很好支撐海量客戶端和主題的開源產品。why?

傳統MQ

雖可通過擴展來支持MQTT協議,但整體架構設計之初,並未考慮支撐海量客戶端和主題。如RocketMQ元數據保存在NameServer的內存,Kafka是保存在zk,這些存儲都不擅長保存大量數據,所以也支撐不了過多客戶端和主題。

另外一些開源MQTT Server

很多就沒集羣功能或集羣功能不完善。集羣功能做的好的產品,大多都把集羣功能放到企業版賣。

所以做MQTT Server技術選型,若你接入IoT設備數量在10w內,可選擇開源產品,選型原則和選擇普通消息隊列一樣,優先選擇一個流行、熟悉的開源產品即可。

若客戶端規模超過10w量級,需支撐這麼大規模客戶端數量,服務端只有單節點肯定不夠,須用集羣,並且這集羣要支持水平擴容。這時就幾乎沒開源產品了,此時只能建議選擇一些雲平臺廠商提供的MQTT雲服務,價格相對較低,也可選擇價格更高商業版MQTT Server。

另外一個選擇就是,基於已有開源MQTT Server,通過一些集成和開發,自行構建MQTT集羣。

5 構建一個支持海量客戶端的MQTT集羣

MQTT集羣如何支持海量在線的IoT設備? 一般來說,一個MQTT集羣它的架構應該是這樣的:

從左向右看,首先接入的地址最好是一個域名,這樣域名後面可配置多個IP地址做負載均衡,當然這域名不是必需。也可直接連負載均衡器。負載均衡可選F5這種專用的負載均衡硬件,也可Nginx這樣軟件,只要是四層或支持MQTT協議的七層負載均衡設備,都可。

負載均衡器後面要部署一個Proxy集羣

Proxy集羣作用

  • 承接海量IoT設備連接
  • 維護與客戶端的會話
  • 作爲代理,在客戶端和Broker之間進行消息轉發

在Proxy集羣后是Broker集羣,負責保存和收發消息。

有的MQTT Server集羣架構:

架構中沒Proxy。實際上,只是把Proxy和Broker功能集成到一個進程,這兩種架構本質沒有太大區別。可認爲就是同一種架構來分析。

前置Proxy,易解決海量連接問題,由於Proxy可水平擴展,只要用足夠多的Proxy節點,就可抗海量客戶端同時連接。每個Proxy和每個Broker只用一個連接通信即可,這對每個Broker來說,其連接數量最多不會超過Proxy節點的數量。

Proxy對於會話的處理,可借鑑Tomcat處理會話的兩種方式:

  • 將會話保存在Proxy本地,每個Proxy節點都只維護連接到自己的這些客戶端的會話。但這要配合負載均衡來使用,負載均衡設備需支持sticky session,保證將相同會話的連接總是轉發到同一Proxy節點
  • 將會話保存在一個外置存儲集羣,如Redis集羣或MySQL集羣。這樣Proxy就可設計成完全無狀態,對負載均衡設備也沒特殊要求。但這要求外置存儲集羣具備存儲千萬級數據能力,同時具有很好性能

如何支持海量主題?

較可行的解決方案,在Proxy集羣的後端,部署多組Broker小集羣,如可以是多組Kafka小集羣,每個小集羣只負責存儲一部分主題。這樣對每個Broker小集羣,主題數量就可控制在可接受範圍內。由於消息是通過Proxy進行轉發,可在Proxy中採用一些像一致性哈希等分片算法,根據主題名稱找到對應Broker小集羣。這就解決支持海量主題的問題。

UML

Proxy的UML圖:

@startuml
package "MQTT Proxy Cluster" {
    class MQTTProxy {
        +handleIncomingMessage()
        +handleOutgoingMessage()
        +produceMessage()
        +consumeMessage()
    }

    class Client {
        +sendMessage()
        +receiveMessage()
    }

    class Broker {
        +publish()
        +subscribe()
    }

    Client --> MQTTProxy
    MQTTProxy --> Broker
}
@enduml

@startuml
actor Client
entity MQTTProxy
entity Broker

Client -> MQTTProxy : sendMessage()
activate MQTTProxy
MQTTProxy -> Broker : produceMessage()
deactivate MQTTProxy
@enduml

@startuml
entity MQTTProxy
entity Broker
actor Client

Broker -> MQTTProxy : publishMessage()
activate MQTTProxy
MQTTProxy -> Client : consumeMessage()
deactivate MQTTProxy
@enduml

<img src="https://p.ipic.vip/wl5sdu.png" style="zoom: 33%;" />

Proxy收發消息的時序圖:

<img src="https://p.ipic.vip/5cqqhw.png" style="zoom:33%;" />

Proxy生產消息流程的時序圖:

<img src="https://p.ipic.vip/swzi3k.png" style="zoom:33%;" />

Proxy消費消息流程的時序圖:

<img src="https://p.ipic.vip/kr60pu.png" alt="image-20231208134111361" style="zoom:33%;" />

6 總結

MQTT是專門爲物聯網設備設計的一套標準的通信協議。這套協議在消息模型和功能上與普通的消息隊列協議是差不多的,最大的區別在於應用場景不同。在物聯網應用場景中,IoT設備性能差,網絡連接不穩定。服務端面臨的挑戰主要是,需要支撐海量的客戶端和主題。

已有的開源的MQTT產品,對於協議的支持都不錯,在客戶端數量小於十萬級別的情況下,可以選擇。對於海量客戶端的場景,服務端必須使用集羣來支撐,可以選擇收費的雲服務和企業版產品。也可以選擇自行來構建MQTT集羣。

自行構建集羣,最關鍵技術點,就是通過前置Proxy集羣解決海量連接、會話管理和海量主題:

  • 前置Proxy負責在Broker和客戶端之間轉發消息,通過這種方式,將海量客戶端連接收斂爲少量的Proxy與Broker之間的連接,解決了海量客戶端連接數的問題
  • 維護會話的實現原理,和Tomcat維護HTTP會話一樣
  • 海量主題,可在後端部署多組Broker小集羣,每個小集羣分擔一部分主題這樣的方式來解決

參考:

本文由博客一文多發平臺 OpenWrite 發佈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章