哈哈哈,終於找到一篇 MQTT 協定的問題了,我寫過一篇關於 MQTT 基礎文章,相信你看完之後就會對 MQTT 有詳細的認識了。
之前有位讀者給我留言說想要了解一下什麽是 MQTT 協定,順便還把我誇了一把,有點不好意思啦。
那麽讀者的要求必須要滿足啊,所以現在 @一下這位小姐姐,來聽課啦!
什麽是 MQTT 協定
MQTT 協定的全稱是 Message Queuing Telemetry Transport ,轉譯為訊息佇列傳輸探測,它是 ISO 標準下的一種基於 釋出 - 訂閱 模式的訊息協定,它是基於 TCP/IP 協定簇的,它是為了改善網路裝置硬體的效能和網路的效能來設計的。MQTT 一般多用於 IoT 即物聯網上,廣泛套用於工業級別的套用場景,比如汽車、制造、石油、天然氣等。
在了解了 MQTT 的概念和套用場景後,我們下來就來走進 MQTT 的學習中了,先來看一下 MQTT 有哪些概念。
MQTT 基礎
上面我們解釋了 MQTT 協定的基本概念,MQTT 協定總結一點就是一種 輕量級的二進制協定 ,MQTT 協定與 HTTP 相比具有一個明顯的優勢: 封包開銷較小 ,封包開銷小就意味著更容易進行網路傳輸。還有一個優勢就是 MQTT 在客戶端容易實作,而且具有易用性,非常適合當今資源有限的裝置。
你可能對這些概念有些諱莫如深,為什麽具有 xxx 這種特性呢?這就需要從 MQTT 的設計說起了。MQTT 協定由 Andy Stanford-Clark (IBM) 和 Arlen Nipper(Arcom,現為 Cirrus Link)於 1999 年發明。 他們需要一種透過衛星連線石油管道的協定,以最大限度地減少電池損耗和頻寬。所以他們為這個協定規定了幾種要求:
這些設計也是 MQTT 的精髓所在,MQTT 經過不斷的發展,已經成為了物聯網 IoT 所必備的一種訊息探測協定,官方強烈推薦使用的版本是 MQTT 5。
釋出 - 訂閱模式
釋出 - 訂閱模式我相信接觸訊息中介軟體架構的同學都聽過,這是一種傳統的 客戶端 - 伺服器 架構的替代方案,因為一般傳統的客戶端-伺服器是客戶端能夠直接和伺服器進行通訊。
但是釋出 - 訂閱模式
pub/sub
就不一樣了,釋出訂閱模式會將發送訊息的釋出者
publisher
與接收訊息的訂閱者
subscribers
進行分離,publisher 與 subscribers 並不會直接通訊,他們甚至都不清楚對方是否存在,他們之間的交流由第三方元件
broker
代理。
pub/sub 最重要的方面是 publisher 與 subscriber 的解藕,這種耦合度有下面三個維度:
Synchronization
解藕:兩個元件的操作比如 publish 和 subscribe 都不會在釋出或者接收過程中產生中斷。
總之,釋出/訂閱模式消除了傳統客戶-伺服器之間的直接通訊,把通訊這個操作交給了 broker 進行代理,並在空間、時間、同步三個維度上進行了解藕。
可拓展性
pub/sub 比傳統的客戶端-伺服器模式有了更好的拓展,這是由於 broker 的高度
並列化
,並且是基於
事件驅動
的模式。可拓展性還體現在訊息的緩存和訊息的智慧路由,還可以透過集群代理來實作數百萬的連線,使用負載均衡器將負載分配到更多的單個伺服器上,這就是 MQTT 的深度套用了。
事件驅動是一種
編程範式
,編程範式是軟體工程中的概念,
它指的是一種編程方法或者說程式設計方式
,比如說物件導向編程和程序導向編程就是一種編程範式,事件驅動中的程式流程會由諸如使用者操作(點選滑鼠、鍵盤)、傳感器輸出或者從其他程式或傳遞的訊息事件決定。事件驅動編程是圖形化使用者介面和其他應用程式比如 Web 中使用的主要範式,這些應用程式能夠響應使用者輸入執行某些操作為中心,這同時也適用於驅動程式的編程。
訊息過濾
在 pub/sub 的架構模式中,broker 扮演著至關重要的作用,其中非常重要的一點就是 broker 能夠對訊息進行過濾,使每個訂閱者只接收自己感興趣的訊息。
broker 有幾個可以過濾的選項
基於主題的過濾
MQTT 是基於 subject 的訊息過濾的,每條訊息都會有一個 topic ,接收客戶端會向 borker 訂閱感興趣的 topic,訂閱後,broker 就會確保客戶端收到釋出到 topic 中的訊息。
在基於內容的過濾中,broker 會根據特定的內容過濾訊息,接受客戶端會經過過濾他們感興趣的內容。這種方法的一個顯著的缺點就是必須事先知道訊息的內容,不能加密或者輕易修改。
在使用物件導向的語言時,基於訊息(事件)的型別過濾是一種比較常見的過濾方式。
為了釋出/訂閱系統的挑戰,MQTT 具有三個服務品質級別,你可以指定訊息從客戶端傳到 broker 或者從 broker 傳到客戶端,在 topic 的訂閱中,會存在 topic 沒有 subscriber 訂閱的情況,作為 broker 必須知道如何處理這種情況。
MQTT 與訊息佇列的區別
我們現在知道,MQTT 是一種訊息佇列傳輸探測協定,這種協定是看似是以訊息佇列為基礎,但卻與訊息佇列有所差別。
在傳統的訊息佇列模式中,一條訊息會儲存在訊息佇列中等待被消費,每個傳入的訊息都儲存在訊息佇列中,直到它被客戶端(通常稱之為消費者)所接收,如果沒有客戶端消費訊息的話,這條訊息就會存在訊息佇列中等待被消費。但是在訊息佇列中,不會存在訊息沒有客戶端消費的情況,但是在 MQTT 中,確存在 topic 無 subscriber 訂閱的情況。
在傳統的訊息佇列模式中,一條訊息只能被一個客戶端所消費,
負載
會分布在佇列的每個消費者之間;而在 MQTT 中,每個訂閱者都會受到訊息,每個訂閱者有相同的負載。
在傳統的訊息佇列模式中,必須使用單獨的命令來顯式建立佇列,只有佇列建立後,才可以生產或者消費訊息;而在 MQTT 中,topic 比較靈活,可以即時建立。
HiveMQ 現在是開源的,HiveMQ 社群版實作了 MQTT broker 規範,並相容了 MQTT 3.1、3.1.1 和 MQTT 5。HiveMQ MQTT Client 是一個基於 Java 的 MQTT 客戶端實作,相容 MQTT 3.1.1 和 MQTT 5。這兩個計畫都可以在 HiveMQ 的 github https:// github.com/hivemq 上找到。我們知道,broker 將 publisher 和 subscriber 進行分離,因此客戶端的連線由 broker 代理,所以在我們深入理解 MQTT 之前,我們需要先知道客戶端和代理的含義。
MQTT 重要概念
MQTT client
當我們討論關於客戶端的概念時,一般指的就是 MQTT Client ,publisher 和 subscriber 都屬於 MQTT Client。之所以有釋出者和訂閱者這個概念,其實是一種相對的概念,就是指當前客戶端是在釋出訊息還是在接收訊息, 釋出和訂閱的功能也可以由同一個 MQTT Client 實作 。
MQTT 客戶端是指執行 MQTT 庫並透過網路連線到 MQTT broker 的任何裝置,這些裝置可以從微控制器到成熟的伺服器。基本上,任何使用 TCP/IP 協定使用 MQTT 裝置的都可以稱之為 MQTT Client。MQTT 協定的客戶端實作非常簡單直接。易於實施是 MQTT 非常適合小型裝置的原因之一。 MQTT 客戶端庫可用於多種程式語言。 例如,Android、Arduino、C、C++、C#、Go、iOS、Java、JavaScript 和 .NET。
MQTT broker
與 MQTT client 對應的就是 MQTT broker,broker 是任何釋出/訂閱機構的核心,根據實作的不同,代理可以處理多達數百萬連線的 MQTT client。
broker 負責接收所有訊息,過濾訊息,確定是哪個 client 訂閱了每條訊息,並將訊息發送給對應的 client,broker 還負責保存會話數據,這些封包括訂閱的和錯過的訊息。broker 還負責客戶端的身份驗證和授權。
MQTT Connection
MQTT 是基於 TCP/IP 協定基礎之上的,所以 MQTT 的 client 和 broker 都需要 TCP/IP 協定的支持。
MQTT 的連線總是在 client 和 broker 之間進行,client 和 client 之間並不會相互連線。如果要發起連線的話,那麽 client 就會向 broker 發起
CONNECT
訊息,代理會使用
CONNACK
訊息和狀態碼進行響應。一旦 client 和 broker 的連線建立後,broker 就會使客戶端的連線一直處於開啟狀態,直到 client 發出斷開命令或者連線中斷。
訊息報文
MQTT 的訊息報文主要分為 CONNECT 和 CONNACK 訊息。
CONNECT
我們上面提到了為了初始化連線,需要 client 向 broker 發送 CONNECT 訊息,如果這個 CONNECT 訊息格式錯誤或者開啟套接字(因為基於 TCP/IP 協定棧需要初始化 Socket 連線)時間過長,亦或是發送連線訊息時間過長的話,broker 就會關閉這條連線。
一個 MQTT 客戶端發送一條 CONNECT 連線,這條 CONNECT 連線可能會包含下面這些資訊:
我這裏解釋一下這些資訊都是什麽概念
ClientId
:顯而易見,這個就是每個客戶端的 ID 標識,也就是連線到 MQTT broker 的每個 client。這個 ID 應該是每個 client 和 broker 唯一的,如果你不需要 broker 持有狀態的話,你可以發送一個空的 ClientId,空的 ClientId 會沒有任何狀態。在這種情況下,
ClientSession
需要設定為 true,否則將會拒絕連線。
CleanSession
:CleanSession 會話標誌會告訴 broker client 是否需要建立持久會話。在持久會話 (CleanSession = false)中,broker 儲存 client 的所有訂閱以及
服務品質(Qos)
是 1 或 2 訂閱的 client 的所有遺失的訊息。如果會話不是持久的(CleanSession = true),那麽 broker 則不會為 client 儲存任何內容並且會清除先前持久會話中的所有資訊。
Username/Password
:MQTT 會發送 username 和 password 進行 client 認證和授權。如果此資訊沒有經過加密或者 hash ,那麽密碼將會以純文本的形式發送。所以,一般強烈建議 username 和 password 要經過加密安全傳輸。像 HiveMQ 這樣的 broker 可以與 SSL 證書進行身份驗證,因此不需要使用者名稱和密碼。
LastWillxxx
:LastWillxxx 表示的是遺願,client 在連線 broker 的時候將會設立一個遺願,這個遺願會保存在 broker 中,當 client 因為
非正常原因
斷開與 broker 的連線時,broker 會將遺願發送給訂閱了這個 topic(訂閱遺願的 topic)的 client。
keepAlive
:keepAlive 是 client 在連線建立時與 broker 通訊的時間間隔,通常以秒為單位。這個時間指的是 client 與 broker 在不發送訊息下所能承受的最大時長。
在聊完 client 與 broker 之間發送建立連線的 CONNECT 訊息後,我們再來聊一下 broker 需要對 CONNECT 進行確認的 CONNACK 訊息。
CONNACK
當 broker 收到 CONNECT 訊息時,它有義務回復 CONNACK 訊息進行響應。CONNACK 訊息包括兩部份內容
SessionPresent
:會話當前標識,這個標誌會告訴 client 當前 broker 是否有一個永續性會話與 client 進行互動。SessionPresent 標誌和 CleanSession 標誌有關,當 client 在 CleanSession 設定為 true 的情況下連線時,SessionPresent 始終為 false,因為沒有永續性會話可以使用。如果 CleanSession 設定為 false,則有兩種可能性,如果 ClientId 的會話資訊可用,並且 broker 已經儲存了會話資訊,那麽 SessionPresent 為 true,否則如果沒有 ClientId 的任何會話資訊,那麽 SessionPresent 為 false。
ReturnCode
:CONNACK 訊息中的第二個標誌是連線確認標誌。這個標誌包含一個返回碼,告訴客戶端連線嘗試是否成功。連線確認標誌有下面這些選項。
關於每個連線的詳細說明,可以參考 https:// docs.oasis-open.org/mqt t/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718035
訊息型別
釋出
當 MQTT client 在連線到 broker 之後就可以發送訊息了,MQTT 使用的是基於 topic 主題的過濾。每條訊息都應該包含一個 topic ,broker 可以使用 topic 將訊息發送給感興趣的 client。除此之外,每條訊息還會包含一個
負載(Payload)
,Payload 中包含要以字節形式發送的數據。
MQTT 是數據無關性的,也就是說數據是由釋出者 - publisher 決定要發送的是 XML 、JSON 還是二進制數據、文本數據。
MQTT 中的 PUBLISH 訊息結構如下。
Packet Identifier
:這個 PacketId 標識在 client 和 broker 之間唯一的訊息標識。packetId 僅與大於零的 Qos 級別相關。
TopicName
:主題名稱是一個簡單的字串,
/
代表著分層結構。
Qos
:這個數位表示的是服務品質水平,服務品質水平有三個等級:0、1 和 2,服務級別決定了訊息到達 client 或者 broker 的保證型別,來決定訊息是否遺失。
RetainFlag
:這個標誌表示 broker 將最近收到的一條 RETAIN 標誌位為
true
的訊息保存在伺服器端(記憶體或者檔)。
true
的訊息。也就是說,如果MQTT 伺服器上已經為某個 Topic 保存了一條 Retained 訊息,當客戶端再次釋出一條新的 Retained 訊息時,那麽伺服器上原來的那條訊息會被覆蓋。
Payload
:這個是每條訊息的實際內容。MQTT 是數據無關性的。可以發送任何文本、影像、加密數據以及二進制數據。
Dupflag
:這個標誌表示該訊息是重復的並且由於預期的 client 或者 broker 沒有確認所以重新發送了一次。這個標誌僅僅與 Qos 大於 0 相關。
當 client 向 broker 發送訊息時,broker 會讀取訊息,根據 Qos 的級別進行訊息確認,然後處理訊息。處理訊息其實就是確定哪些 subscriber 訂閱了 topic 並將訊息發送給他們。
最初釋出訊息的 client 只關心將 PUBLISH 訊息發送給 broker,一旦 broker 收到 PUBLISH 訊息,broker 就有責任將其傳遞給所有 subscriber。釋出訊息的 client 不會知道是否有人對釋出的訊息感興趣,同時也不知道多少 client 從 broker 收到了訊息。
訂閱
client 會向 broker 發送 SUBSCRIBE 訊息來接收有關感興趣的 topic,這個 SUBSCRIBE 訊息非常簡單,它包含了一個唯一的封包標識和一個訂閱列表。
Packet Identifier
:這個 PacketId 和上面的 PacketId 一樣,都表示訊息的唯一識別元。
ListOfSubscriptions
:SUBSCRIBE 訊息可以包含一個 client 的多個訂閱,每個訂閱都會由一個 topic 和一個 Qos 構成。訂閱訊息中的 topic 可以包含通配符。
確認訊息
client 在向 broker 發送 SUBSCRIBE 訊息後,為了確認每個訂閱,broker 會向 client 發送 SUBACK 確認訊息。這個 SUBACK 包含原始 SUBSCRIBE 訊息的 packetId 和返回碼列表。
其中
Packet Identifier
:這個封包識別元和 SUBSCRIBE 中的相同。
ReturnCode
:broker 為每個接收到的 SUBSCRIBE 訊息的 topic/Qos 對發送一個返回碼。例如,如果 SUBSCRIBE 訊息有五個訂閱訊息,則 SUBACK 訊息包含五個返回碼作為響應。
到現在我們已經探討過了三種訊息型別,釋出 - 訂閱 - 確認訊息,這三種訊息的示意圖如下。
退訂
SUBSCRIBE 訊息對應的是
UNSUBSCRIBE
訊息,這條訊息發送後,broker 會刪除關於 client 的訂閱。所以,UNSUBSCRIBE 訊息與 SUBSCRIBE 訊息類似,都具有 packetId 和 topic 列表。
確認退訂
取消訂閱也需要 broker 的確認,此時 broker 會向 client 發送一個
UNSUBACK
訊息,這個 UNSUBACK 訊息非常簡單,只有一個 packetId 數據識別元。
退訂和確認退訂的流程如下。
當 client 收到來自 broker 的 UNSUBACK 訊息後,就可以認為 UNSUBSCRIBE 訊息中的訂閱被刪除了。
聊聊 Topic
聊了這麽多關於 MQTT 的內容,但是我們還沒有好好聊過 Topic。在 MQTT 中,Topic 是指 broker 為每個連線的 client 過濾訊息的
UTF-8
字串。Topic 是一種分層的結構,可以由一個或者多個 Topic 組成。每個 Topic 由
/
進行分割。
與傳統的訊息佇列相比,MQTT Topic 非常輕量級,client 在釋出或訂閱之前不需要先建立所需要的 Topic,broker 在接收每個 Topic 前不用進行初始化操作。
通配符
當客戶端訂閱 Topic 時,它可以訂閱已釋出訊息的確切 Topic,也可以使用通配符來同時訂閱多個 Topic。通配符有兩種: 單級和多級 。
單級通配符
單級通配符可以替換 Topic 的一個級別,
+
號代表 Topic 中的單級通配符。
如果 Topic 包含任意字串而不是通配符,則任何 Topic 都能夠和單級通配符匹配。例如
myhome/groundfloor/+/temperature 就有下面這幾種匹配方式。
多級通配符
多級通配符涵蓋多個 Topic,
#
代表 Topic 中的多級通配符。為了讓 broker 能夠確定和哪些 Topic 匹配,多級通配符必須作為 Topic 中的最後一個字元放置,並以
/
開頭。
下面是 myhome/groundfloor/# 的幾個例子
當 client 訂閱帶有多級通配符的 Topic 時,不論 Topic 有多長多深,它都會收到通配符之前 Topic 的所有訊息。如果你只將 Topic 定義為 # 的話,那麽你將會收到所有的訊息。
另外,我自己總結了一些原創優質內容,現在免費分享給你,你可以在下面連結中獲取。
連結: https://
pan.baidu.com/s/1xXlSMY
TT9W7mexr_KHU-rA
密碼: 7im6
順便推薦一下自己的優質文章:
電腦基礎知識對程式設計師來說有多重要?
cxuan 又瞎 TM 寫了兩本 PDF。
cxuan 嘔心瀝血肝了四本 PDF。