一文讀懂物聯網 MQTT 協議之基礎特性篇

一、前言

上個月有個讀者問我物聯網 MQTT 協議實戰相關的問題,我說後面會搞,沒想到不知不覺一個月了,太忙了,再怎麼忙答應的事情還是要給讀者一個交代,所以就有了此文。

二、MQTT 協議概要

2.1 什麼是 MQTT 協議

MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸協議),是一種基於發佈 / 訂閱(publish/subscribe)模式的 “輕量級” 通訊協議,該協議構建於 TCP/IP 協議上,由 IBM 於 1999 年發明。MQTT 協議的主要特徵是開放、簡單、輕量級和易於實現,這些特徵使得它適用於受約束的應用環境,如:

通過 MQTT 協議,目前已經擴展出了數十種 MQTT 服務器端程序,可以通過 PHP、Java、Python、C、C# 等語言向 MQTT 發送消息。由於開放源代碼、耗電量小等特點,MQTT 非常適用於物聯網領域,如傳感器與服務器的通信、傳感器信息採集等。

2.2 發佈 / 訂閱模式

發佈 / 訂閱模式並不是 MQTT 協議特有的模式,像我們很多消息中間件都有使用發佈 / 訂閱模式,這裏你是不是想說,這不就是我們所說的觀察者模式嘛,還真不是,這兩個模式很容易混淆。觀察者模式只有 觀察者 + 被觀察者兩個角色,而發佈 / 訂閱模式還有一個經紀人 Broker;往更深層次的講觀察者和被觀察者,是松耦合的關係,而發佈者和訂閱者,則完全不存在耦合。

在客戶端 / 服務器模型中,客戶端直接與服務器端點通信。而發佈 / 訂閱模式 pub/sub 就不一樣了,發佈 / 訂閱模式會將發送消息的發佈者 publisher 與接收消息的訂閱者 subscribers 進行分離,publisher 與 subscribers 並不會直接通信,他們甚至都不清楚對方是否存在,他們之間的交流由第三方組件 broker 代理。


pub/sub 最重要的方面是消息的發佈者與接收者(訂閱者)的解耦。這種解耦有幾個維度:

總之,發佈 / 訂閱模式消除了傳統客戶端 / 服務器之間的直接通信,把通信這個操作交給了 broker 進行代理,並在空間、時間、同步三個維度上進行了解耦。

2.3 可擴展性

pub/sub 比傳統的客戶端 / 服務器模式有了更好的拓展,這是由於 broker 的高度並行化,並且是基於事件驅動的模式。可擴展性還體現在消息的緩存和消息的智能路由,還可以通過集羣代理來實現數百萬的連接,使用負載均衡器將負載分配到更多的單個服務器上,這就是 MQTT 的深度應用了。

2.4 消息過濾

很明顯,broker 在 pub/sub 過程中起着舉足輕重的作用。但是代理如何過濾所有消息,以便每個訂閱者只接收感興趣的消息?broker 有幾個可以過濾的選項:

2.5 MQTT 與消息隊列的區別

這裏你又會說了,既然 MQTT 與主流的消息的隊列都採用發佈 / 訂閱模式,那他們就是一樣的。這裏老周得再提一嘴,確實和消息隊列很多相似的地方,但還有有些差異的,下面就來說道說道:

三、MQTT 重要概念

3.1 MQTT Client

publisher 和 subscriber 都屬於 MQTT Client,之所以有發佈者和訂閱者這個概念,其實是一種相對的概念,就是指當前客戶端是在發佈消息還是在接收消息,發佈和訂閱的功能也可以由同一個 MQTT Client 實現。

MQTT 客戶端是運行 MQTT 庫並通過網絡連接到 MQTT 代理的任何設備(從微控制器到成熟的服務器)。例如,MQTT 客戶端可以是一個非常小的、資源受限的設備,它通過無線網絡進行連接並具有一個最低限度的庫。基本上,任何使用 TCP/IP 協議使用 MQTT 設備的都可以稱之爲 MQTT Client。MQTT 協議的客戶端實現非常簡單直接,易於實施是 MQTT 非常適合小型設備的原因之一。MQTT 客戶端庫可用於多種編程語言。例如,Android、Arduino、C、C++、C#、Go、iOS、Java、JavaScript 和 .NET。

3.2 MQTT Broker

與 MQTT Client 對應的就是 MQTT Broker,Broker 是任何發佈 / 訂閱協議的核心,根據實現的不同,代理可以處理多達數百萬連接的 MQTT Client。

Broker 負責接收所有消息,過濾消息,確定是哪個 Client 訂閱了每條消息,並將消息發送給對應的 Client,Broker 還負責保存會話數據,這些數據包括訂閱的和錯過的消息。Broker 還負責客戶端的身份驗證和授權。

3.3 MQTT Connection

MQTT 協議基於 TCP/IP。客戶端和代理都需要有一個 TCP/IP 協議支持。

MQTT 連接始終位於一個客戶端和代理之間。客戶端從不直接相互連接。要發起連接,客戶端向代理發送 CONNECT 消息。代理使用 CONNACK 消息和狀態代碼進行響應。建立連接後,代理將保持打開狀態,直到客戶端發送斷開連接命令或連接中斷。

四、消息列表

4.1 CONNECT

爲了創建連接,客戶端向代理發送命令消息。如果此 CONNECT 消息格式錯誤(根據 MQTT 規範)或打開網絡套接字和發送連接消息之間的時間過長,代理將關閉連接。

一個 MQTT 客戶端發送一條 CONNECT 連接,這條 CONNECT 連接可能會包含下面這些信息:


我們將重點關注以下選項:

4.2 CONNACK

當 broker 收到 CONNECT 消息時,它有義務回覆 CONNACK 消息進行響應。CONNACK 消息包括兩部分內容:


4.3 PUBLISH

MQTT 客戶端可以在連接到 broker 後立即發佈消息,MQTT 使用的是基於 topic 主題的過濾。每條消息都必須包含一個主題,broker 可以使用該主題將消息轉發給感興趣的客戶端。通常,每條消息都有一個負載(Payload),其中包含要以字節格式傳輸的數據。MQTT 是數據無關性的,也就是說數據是由發佈者 - publisher 決定要發送的是 XML 、JSON 還是二進制數據、文本數據。

MQTT 中的 PUBLISH 消息有幾個我們想要詳細討論的屬性:

當客戶端向 MQTT broker 發送消息進行發佈時,broker 讀取消息、確認消息(根據 QoS 級別)並處理消息。broker 的處理包括確定哪些客戶端訂閱了主題並將消息發送給他們。


最初發布消息的客戶端只關心將 PUBLISH 消息傳遞給 broker。一旦 broker 收到 PUBLISH 消息,broker 就有責任將消息傳遞給所有訂閱者。發佈客戶端不會得到關於是否有人對發佈的消息感興趣或有多少客戶端從 broker 收到消息的任何反饋。

4.4 Subscribe

client 會向 broker 發送 SUBSCRIBE 消息來接收有關感興趣的 topic,這個 SUBSCRIBE 消息非常簡單,它包含了一個唯一的數據包標識和一個訂閱列表。

4.5 Suback

爲了確認每個訂閱,broker 向客戶端發送一個 SUBACK 確認消息。該消息包含原始 Subscribe 消息的數據包標識符(以明確標識該消息)和返回碼列表。

4.6 Unsubscribe

SUBSCRIBE 消息的對應是 UNSUBSCRIBE 消息。此消息刪除 broker 上客戶端的現有訂閱。UNSUBSCRIBE 消息與 SUBSCRIBE 消息類似,具有數據包標識符和主題列表。

4.7 Unsuback

爲了確認取消訂閱,broker 向客戶端發送一個 UNSUBACK 確認消息。此消息僅包含原始 UNSUBSCRIBE 消息的數據包標識符(以明確標識該消息)。


客戶端收到來自 broker 的 UNSUBACK 後,可以認爲 UNSUBSCRIBE 消息中的訂閱被刪除了。

五、Topics

前面我們說了很多 MQTT 協議的格式以及消息列表,這一節我們來說下 Topics 主題。主題在 MQTT 中很重要,因爲我們寫代碼的時候往往都是需要先確認好 MQTT 的 Topics。

在 MQTT 中,主題一詞是指 broker 用於爲每個連接的客戶端過濾消息的 UTF-8 字符串。主題由一個或多個主題級別組成。每個主題級別由正斜槓(主題級別分隔符)分隔。


與消息隊列相比,MQTT 主題非常輕量級。客戶端在發佈或訂閱它之前不需要創建所需的主題。broker 接受每個有效主題而無需任何事先初始化。

5.1 通配符

當客戶端訂閱主題時,它可以訂閱已發佈消息的確切主題,也可以使用通配符同時訂閱多個主題。通配符只能用於訂閱主題,不能用於發佈消息。有兩種不同類型的通配符:單級和多級。

    顧名思義,單級通配符替換一個主題級別。加號代表主題中的單級通配符。
       

     如果主題包含任意字符串而不是通配符,則任何主題都與具有單級通配符的主       題匹配。例如,訂閱 myhome/groundfloor/+/temperature 可以產生以下結果:

5.2 以 $ 開頭的主題

通常,您可以根據需要命名 MQTT 主題。但是,有一個例外:以 符號開頭的主題具有不同的目的。當您將多級通配符作爲主題 (#) 訂閱時,這些主題不是訂閱的一部分。$-symbol 主題保留用於 MQTT 代理的內部統計信息。客戶端無法向這些主題發佈消息。目前,此類主題尚無官方標準化。通常,$SYS/用於所有以下信息,但代理實現各不相同。MQTT GitHub wiki 中提供了對 $SYS-topics 的一項建議 。這裏有些例子:

$SYS/broker/clients/connected
 $SYS/broker/clients/disconnected
 $SYS/broker/clients/total
 $SYS/broker/messages/sent
 $SYS/broker/uptime


可以呀,看到了最後面。授人以魚不如授人以漁,下面是一個關於 MQTT Version 3.1.1 的介紹,有些協議格式詳細的可以前往查看。

https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718035

歡迎大家關注我的公衆號【老周聊架構】,Java 後端主流技術棧的原理、源碼分析、架構以及各種互聯網高併發、高性能、高可用的解決方案。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Uago8zL2DXthPYpe4ZjXww