為什麼最近每份 Android 簡歷都說 「熟悉 MQTT 協議」?
請點贊關注,你的支援對我意義重大。
🔥 Hi,我是小彭。本文已收錄到 GitHub · AndroidFamily 中。這裡有 Android 進階成長知識體系,有志同道合的朋友,關注公眾號 [彭旭銳] 帶你建立核心競爭力。
前言
大家好,我是小彭。
MQTT 是一種基於發布 – 訂閱模型的消息傳遞協議,在物聯網和移動應用有較廣泛的應用。如果你的目標是衝擊中高級工程師崗位,MQTT 或許是一個不錯的亮點。最近,我還發現很多候選人會在簡歷中寫自己 「熟悉 MQTT 協議」,但多數人只是停留在了解或用過的程度。
這篇文章里,我將與你探討 MQTT 協議的 工作原理 & 協議消息格式 & 核心特性,實戰的部分我們會在下篇文章中討論。如果能幫上忙,請務必點贊加關注,給小彭一點創作的動力。
記錄:2022 年 9 月 9 日修訂:優化文章結構
學習路線圖:
1. 認識 MQTT
1.1 什麼是 MQTT?
MQTT (Message Queuing Telemetry Transport,消息隊列遙測傳輸) 是一種基於 TCP/IP 協議族的應用層協議。MQTT 協議是專門針對硬體性能低下 & 網路狀況不穩定的場景設計的,這使得 MQTT 在物聯網和移動應用等受限場景得到廣泛應用。
1.2 MQTT 協議的發展歷史
- 1999 年:Andy Stanfork-Clark (IBM) 和 Arlen Nipper 發布 MQTT 協議,用於通過衛星連接石YouTube道遙測系統,MQTT 中的 TT (Telemetry Transport) 就是源於這樣一個遙測系統;
- 2010 年:MQTT 協議免費發布;
- 2014 年:MQTT 協議正式成為 OASIS 標準,經過多年的發展,MQTT 協議已經成為互聯網 (IoT) 的主要協議之一。
目前,MQTT 主要分為兩個大版本:
1.3 MQTT 協議的工作模型
MQTT 是基於發布 – 訂閱模型 (pub/sub) 的消息傳遞協議,與請求 – 響應模型不同,發布 – 訂閱模型主要有三種角色:publisher & subscriber & subscriber:
-
publisher & subscriber (發布者 & 訂閱者): 是指通過網路連接到 MQTT broker 的設備,也叫 客戶端 (client)。一個客戶端既可以作為消息發布者,也可以作為消息訂閱者;
-
broker (代理): 代理是整個發布 – 訂閱模型的核心,也叫 服務端。
當 client 發布某個主題的消息時,broker 會將該消息分發給任何已訂閱該主題的 client。通常來說,client 不會存儲消息,一旦消息被發送到這些 client,消息就會從 broker 上刪除。另外,保留消息、持久連接和服務品質 QoS 可能會導致消息臨時存儲在 broker 上。
發布 – 訂閱模式使得 消息的發布者和訂閱者解耦,主要體現為空間解耦和時間解耦:
-
空間解耦 / 設備解耦: 發布者和訂閱者通過 broker 進行消息傳遞,相互之間感知不到對方的存在。當一個客戶端斷線時,整個系統可以繼續工作;
-
時間解耦: publisher 和 subscriber 不一定需要同時運行;
圖片引用自 //juejin.cn/post/6976441705067184135 —— cxuan 著
1.4 MQTT 協議和 HTTP 協議有什麼區別?
特性 | MQTT 協議 | HTTP 協議 |
---|---|---|
傳輸層 | TCP | TCP 或 UDP |
分發模型 | 發布 – 訂閱模型 | 請求 – 響應模型 |
分發關係 | 1 對 0/1/N | 1 對 1 |
數據安全 | 使用 SSL/TLS | 不一定採用 HTTPS |
加密 | 應用層對有效載荷加密 | 不在應用層加密 |
消息頭大小 | 較小 | 較大 |
-
1、MQTT 協議基於傳輸層 TCP 協議,而 HTTP 可以基於 TCP 或 UDP(HTTP/3);
-
2、MQTT 協議採用發布 – 訂閱模型,同一個設備既可以是發布者也可以是訂閱者;而 HTTP 協議採用請求 – 響應模型,一個設備作為請求方,另一個設備作為響應方;
-
3、MQTT 消息分發可以是 1 對 0/1/N,而 HTTP 消息分發是 1 對 1;
-
4、MQTT 協議使用 SSL/TLS 來確保安全,而 HTTP 協議並不規定使用 HTTPS;
-
5、MQTT 協議在應用層對有效載荷 (payloads) 加密,而 HTTPS 協議不在應用層加密,在傳輸數據前不會加密數據;
-
6、MQTT 消息頭較小,而 HTTP 消息頭較大(HTTP/2 有頭部壓縮);
1.5 為什麼 MQTT 協議適合物聯網和移動應用場景?
物聯網和移動應用場景的特點是硬體性能低下和網路狀況不穩定,而 MQTT 協議就是專門針對這種環境設計的,主要在四個方面有優勢:
-
1、架構設計: MQTT 協議採用發布 – 訂閱模型,使得消息發布者和消息訂閱者互相解耦,當一個客戶端斷線時,整個系統可以繼續工作。這使得 MQTT 在網路品質的場景下更具優勢;
-
2、消息大小: MQTT 協議具有非常小的消息頭,這使得 MQTT 協議更適應低頻寬網路環境;
-
3、交付能力: MQTT 協議提供了更豐富的消息交付保證能力,它定義了三種消息發布服務品質 (QoS):「最多發一次」、「最少發一次」 和 「正好發一次」。其中,「正好一次」 用於計費系統和 IM App 推送中,能確保用戶收到且只收到一次;
-
4、間歇性連接: MQTT 提供了遺囑消息和保留消息的特性。遺囑消息使得客戶端端斷開連接時,所有訂閱的客戶端都能收到來自代理的消息;保留消息意味著新訂閱的客戶端可以立即獲得保留的消息(類似粘性消息)。這使得 MQTT 協議更適合網路不穩定的 間歇性連接的場景。
1.6 誰更適合物聯網(HTTP/2 & WebSocket & MQTT)?
-
HTTP/2 是 HTTP/1.x 的升級,主要體現在:利用 「多路復用和二進位分幀」 來解決隊首阻塞問題,降低了通訊時延;利用 「頭部壓縮」 減少消息頭部,降低了傳輸開銷;實現了 伺服器推送,允許在不發起請求的情況下將數據推送到客戶端,彌補了 Http/1.x 依賴 Websockets 才能實現推送的缺陷。這些改進使得 HTTP/2 也具有適應物聯網場景的條件;
-
WebSockets 是在 Web 瀏覽器和 Web 伺服器之間進行握手的協議,它降低了使用 Http/1.x 進行雙工通訊的開銷。隨著 HTTP/2 成為標準,對 websockets 的需求可能會下降;
-
MQTT 是基於發布訂閱模型的協議,因其頻寬消耗小而被廣泛應用於物聯網協議。
結論:這三種協議並沒有絕對的優勝者,最好的協議取決於具體的需求和限制條件。但如果只從頻寬、電池、功能多樣性這些基本條件看,MQTT 在其中是更佔優的選擇。例如,我司的 IM 產品在 App 端是採用 MQTT 協議的實現,而在 Web 端因為有良好的 WebSocket 能力基礎,所以採用的是 WebSocket 傳輸 MQTT 格式消息。
1.7 為什麼 MQTT 協議基於 TCP,可以基於 UDP 協議嗎?
MQTT 協議的設計特性中包含了一項 「高可靠性交付」,它需要一個保證可靠的底層傳輸層協議,因此 TCP 協議、TLS 協議、WebSocket 協議都可能作為 MQTT 的底層協議。而無連接的 UDP 協議會丟失或重排數據,不能滿足 MQTT 協議的傳輸需要。
2. MQTT 協議消息格式
2.1 MQTT 協議消息的特點
-
1、基於二進位: MQTT 是一種基於二進位的協議,所謂基於二進位,是指 MQTT 協議操作的元素是二進位數據而不是文本數據;
-
2、命令 & 命令確認格式: MQTT 消息採用命令 & 命令確認的格式,每個命令消息都有一個關聯的命令確認消息,兩個消息之間會通過一個 」包唯一標識「 欄位進行關聯???TODO。這與 TCP 的報文確認應答機制是類似的,不過兩者的顆粒度是不同的,MQTT 是對整個應用層消息的確認,而 TCP 是對傳輸層報文段的確認,或者說是對序列號的確認;
-
3、消息頭很小: MQTT 消息頭最小只需要 2 位元組。
2.2 MQTT 協議消息基本結構
一個 MQTT 消息由三部分組成:
MQTT 消息結構 | 描述 | 長度 |
---|---|---|
固定報頭(Fixed header) | 存在於所有 MQTT 消息中 | 2 到 5 位元組 |
可變報頭(Variable header) | 存在於部分 MQTT 消息中 | 0 或 N 位元組 |
載荷(Payloads) | 存在於部分 MQTT 消息中 | 0 或 N 位元組 |
1、固定報頭
所有 MQTT 消息都包含一個固定報頭,固定報頭由消息類型、標誌位和剩餘長度三個部分。固定報頭長度為 2 ~ 5 位元組,具體取決於 「剩餘長度(Remaining Length)」 的大小,剩餘長度表示當前消息剩餘部分的位元組數,包括可變報頭和有效載荷的長度,但不包括剩餘長度欄位本身的位元組數。
提示: 如何判斷剩餘長度的位元組數,採用的是前綴無歧義的表示法。
固定報頭格式如下:
MQTT 消息類型(MQTT Control Packet type)匯總如下:
消息類型 | 值 | 消息流轉方向 | 描述 | 需要有效載荷 |
---|---|---|---|---|
Reserved | 0 | 禁止 | 保留 | / |
CONNECT | 1 | => | 客戶端請求連接伺服器 | ✔ |
CONNACK | 2 | <= | CONNECT 消息確認 | ✖ |
PUBLISH | 3 | <==> | 客戶端發布消息 | 可選 |
PUBACK | 4 | <==> | PUBLISH 消息確認(QoS 1) | ✖ |
PUBREC | 5 | <==> | 發布收到(保證交付第一步) | ✖ |
PUBREL | 6 | <==> | 發布釋放(保證交付第二步) | ✖ |
PUBCOMP | 7 | <==> | 發布完成(保證交付第三步) | ✖ |
SUBSCRIBE | 8 | => | 客戶端訂閱消息 | ✔ |
SUBACK | 9 | <= | SUBSCRIBE 消息確認 | ✔ |
UNSUBSCRIBE | 10 | => | 客戶端取消訂閱 | ✔ |
UNSUBACK | 11 | <= | UNSUBSCRIBE 消息確認 | ✖ |
PINGREQ | 12 | => | 心跳請求 | ✖ |
PINGRESP | 13 | <= | PINGREQ 消息確認 | ✖ |
DISCONNECT | 14 | => | 客戶端斷開連接 | ✖ |
Reserved | 15 | 禁止 | 保留 | / |
2、可變報頭
不同消息的可變報頭內容不一樣,不過其中有一個比較通用的欄位:
- 包唯一標識(Packet Identifier): SUBSCRIBE,UNSUBSCRIBE,PUBLISH(QoS > 0)的消息中會包含一個 2 位元組的唯一標識欄位,每次 client 發送這些消息時,必須分配一個未使用過的唯一標識。而這些消息的應答消息,如 PUBACK、PUBREC、PUBREL、UNSUBACK 必須與對應消息攜帶相同的唯一標識。
3、載荷
某些 MQTT 消息會包含一個有效載荷,對於 PUBLISH 消息來說,有效載荷就是應用消息。
3. MQTT 協議消息類型詳解
上一節,我們提到在 MQTT 固定報文頭部中會標記 MQTT 消息類型(MQTT Control Packet type) ,這一節我們具體討論下這些消息類型。
3.1 連接消息
MQTT 的連接總是發生在 client 和 broker 之間,兩個 client 之間不會互相感知。請求連接時,client 會向 broker 發送 CONNECT
連接消息,broker 接受連接後會響應 CONNACK
連接確認消息。一旦連接建立,連接會一直保持打開狀態,直到 client 發送 DISCONNECT
斷開連接消息或連接異常中斷。
CONNECT 請求連接:
CONNECT
是 client 發送給 broker 的首個消息,並且在一次連接中,client 只能發送一次 CONNECT
消息,發送的第二個 CONNECT
消息會被 broker 當作違反協議處理,並斷開連接。在 CONNECT
消息中,主要包含以下內容:
-
ClientId 客戶端名稱: 所有 client 都需要一個名稱,broker 會根據 client 名稱來跟蹤會話,因此 client 名稱必須是 唯一的。如果連接到 broker 時已經有一個重名的 clientId,那麼會先斷開現有 client 的連接,這將可能導致斷開和連接的死循環,因為大多數 MQTT client 有斷線重連機制;
-
CleanSession 持久會話: 當 client 連接到 broker 時,可以使用持久連接或非持久連接,
CleanSession
標誌決定是否使用持久連接(當CleanSession = 0
時表示持久連接),對於持久會話,broker 會存儲會話狀態;而對於非持久會話,broker 不會存儲 client 的任何內容,具體見第 4.2 節 · 會話狀態; -
UserName & Password 用戶名 & 密碼: 用於 broker 認證和授權;
-
KeepAlive 保活探測間隔: KeepAlive 是以秒單位的時間間隔,指 client 發送兩次消息的最大時間間隔,當 client 和 borker 之間在一段時間內沒有數據交互時,client 會發送 PINGREQ 探測消息 用於判斷連接是否正常,來決定是否要關閉該連接。KeepAlive 是 MQTT 協議的保活機制,從作用上看與 TCP 的 Keepalive 保活機制是非常類似的,不過 MQTT 協議的保活機制是應用層 client 實現的,而 TCP 的保活機制是 「內核」 實現的。
-
Last Will Message 遺囑消息: 遺囑消息用於通知意外停機的 client,每個 client 在連接時可以設置一個遺囑消息,這個遺囑消息會存儲在 broker 上。當 client 因 「非正常原因」 斷開連接時,broker 會將遺囑消息分發給訂閱了
「Will」
主題的 client。另外,這條遺囑消息還可以設置是否為保留消息(Will Retain
標誌)以及服務品質等級(Will Qos
)。
CONNACK 連接確認:
CONNACK
消息用於確認 CONNECT
消息。CONNECT
是 client 發送給 broker 的首個消息,相應地,broker 發送給 client 的首個消息一定是 CONNACK
消息。在 CONNACK
消息中,主要包含以下內容:
-
SessionPresent 持久會話:
SessionPresent
標誌表示當前 broker 是否持有與 client 的持久會話。當 broker 接收了一個非持久會話連接(CleanSession = 1
),SessionPresent 的值始終為 0;而當 broker 接收了一個持久會話連接(CleanSession = 0
),則 SessionPresent 的值取決於 broker 是否存儲了 ClientId 的會話狀態; -
ReturnCode 響應碼: 用於表示連接請求是否成功,如果響應碼不為 0,則表示連接失敗。
具體取值如下表:
返回碼 | 描述 |
---|---|
0 | 連接已接受 |
1 | 連接被拒絕,不可接受的協議版本 |
2 | 連接被拒絕,標識符被拒絕 |
3 | 連接被拒絕,伺服器不可用 |
4 | 連接被拒絕,用戶名或密碼錯誤 |
5 | 連接被拒絕,未授權 |
DISCONNECT 斷開連接:
DISCONNECT
消息由 client 發送給 broker,用於斷開連接。DISCONNECT 消息沒有可變報頭和有效載荷,也沒有對應的確認應答消息,表示一個乾淨利索地斷開連接操作。斷開連接後,client 不能再發送除 CONNECT
消息之外的消息,broker 也需要丟棄和當前會話的遺囑消息。
3.2 訂閱消息
MQTT 是基於發布訂閱模型的協議,在建立連接後,client 可以向 broker 訂閱感興趣的一個或多個話題。
3.2.1 SUBSCRIBE 訂閱
SUBSCRIBE
消息由 client 發送給 broker,用於訂閱感興趣的話題,SUBSCRIBE
消息主要包含以下內容:
- 主題過濾器列表:
SUBSCRIBE
消息的有效載荷中至少需要包含一個話題過濾器,每個過濾器由一個 Topic 和 QoS 組成,其中的 QoS 指定了指定 client 接受的最大 OoS 等級。
3.2.2 SUBACK 訂閱確認
SUBACK
消息用於確認 SUBSCRIBE
消息。SUBACK
消息主要包含以下內容:
- 返回碼列表: 每個返回碼都與 SUBSCRIBE 消息中的話題過濾器一一對應。
具體取值如下表:
返回碼 | 描述 |
---|---|
0x00 | 訂閱成功,最大 QoS 為 0 |
0x01 | 訂閱成功,最大 QoS 為 1 |
0x02 | 訂閱成功,最大 QoS 為 2 |
0x80 | 訂閱失敗 |
3.2.3 UNSUBSCRIBE 退訂
UNSUBSCRIBE
消息由 client 發送給 broker,用於退訂不感興趣的話題,UNSUBSCRIBE
消息主要包含以下內容:
- 話題列表: UNSUBSCRIBE 消息的有效載荷中至少需要包含一個話題。
3.2.4 UNSUBACK 退訂確認
UNSUBACK
消息用於確認 UNSUBSCRIBE
消息。UNSUBACK
消息非常簡單,只有一個包唯一標識(位於可變報頭)。
3.3 發布消息
當 MQTT client 在連接到 broker 之後就可以發送消息了,每條 PUBLISH
消息都包含一個 topic ,broker 會根據 topic 將消息發送給感興趣的 client。除此之外,每條消息還會包含一個 Payload,Payload 是真正發布的應用消息,載荷的內容和格式由應用層決定,MQTT 協議層不關心。
3.3.1 PUBLISH 發布
PUBLISH
消息可以由 client 發送給 broker,也可以由 broker 發送給 client,用來運送應用層消息。PUBLISH 消息主要包含以下內容:
-
QoS 發布服務品質標誌: 標記當前
PUBLISH
消息傳送的交付保證水平,分為三個等級,具體見第 4.3 節 · 發布服務品質:- QoS 0(默認): 最多發一次
- QoS 1: 最少發一次
- QoS 2: 正好發一次
-
RETAIN 保留消息標誌: 標記當前
PUBLISH
消息是否為保留消息,當 client 發送給 broker 的 PUBLISH 消息標記 RETAIN = 1 時,broker 會存儲該消息,當新的 client 註冊訂閱時,並且匹配該消息主題時,該保留消息會發送給訂閱者,具體見第 4.4 節 · 保留消息; -
DUP 重傳標誌: 標記當前的
PUBLISH
/PUBREL
消息是否為重複發送消息。 MQTT 協議規定了兩種消息重傳的場景,具體見第 4.5 節 · 消息重傳; -
TopicName 話題名: 表示載荷數據的發布通道;
-
包唯一標識: 只有 QoS1 和 OoS2 的
PUBLISH
消息中存在; -
載荷(應用消息):
PUBLISH
消息的載荷是真正發布的應用消息,載荷的內容和格式由應用層決定,MQTT 協議層不關心。另外,載荷的數據長度等於:固定報頭中的剩餘長度(Remaining Lenght)- 可變報頭的長度,載荷長度也可以為零。
3.3.2 發布確認
PUBLISH 消息的接收方需要發送確認應答,不同 QoS 等級的 PUBLISH 消息響應的消息不同:
發布服務品質等級 QoS | 期望的確認應答 |
---|---|
QoS 0 | 無確認應答 |
OoS 1 | PUBACK 消息 |
OoS 2 | PUBREC 消息 PUBREL 消息 PUBCOMP 消息 |
3.4 Ping 心跳探測
當 client 和 broker 在一段時間內沒有數據交互時,client 會發送 PINGREQ
探測消息,用於判斷連接是否正常,來決定是否要關閉該連接,這就是 MQTT 協議的保活機制。
3.4.1 PINGREQ 探測消息
PINGREQ 消息由 client 發送給 broker。
3.4.2 PINGRESP 探測確認
PINGRESP 消息由 broker 發送給 client,代表 client 是存活的。
4. MQTT 協議核心特性
4.1 主題和主題過濾器
MQTT 主題本質上是一種 「定址形式」,用於將應用層消息分發到期望的客戶端。MQTT 主題是一種類似於文件系統的分層結構,使用 「/」 正斜杠 作為分隔符。
4.1.1 主題格式規範
- 1、區分大小寫;
- 2、採用 UTF-8 編碼的字元串;
- 3、非空字元串,至少包含一個字元才有效;
- 4、可以包含空;
- 5、一個主題增加 「/」 前綴或後綴後是不同主題。
4.1.2 主題通配符
客戶端訂閱主題時,可以訂閱確定的主題(例如 「group/group123」),也可以使用 「通配符」 來同時訂閱多個主題。需要注意的是:在發布消息時不允許使用主題通配符,client 每次發布消息只能發布到單個主題。
- 單級通配符:
+
是單級通配符,單級通配符可以用於任何一個主題級別,但只能匹配一個級別。例如:
主題 | 匹配主題舉例 |
---|---|
group/+/123 | group/vip/123 group/temp/123 |
- 多級通配符:
#
是多級通配符,多級通配符可以匹配多個連續級別。需要注意,多級通配符只能用於主題的最後一個級別。例如:
主題 | 匹配主題舉例 |
---|---|
group/# | group group/123 group/vip/123 group/temp/123 |
4.1.3 $SYS 主題
$SYS
主題是 broker 上默認創建的只讀主題,除此之外,broker 不會默認創建任何主題,所有主題都是由客戶端訂閱或發布才創建的,都不是永久性的。關於 $SYS
主題的更多介紹在 這裡
4.1.4 主題的生存周期
- 創建主題:某個客戶端訂閱該主題,或者某個客戶端向主題發布消息,同時設置為保留消息;
- 刪除主題:訂閱該主題的最後一個客戶端斷開連接,同時連接為非持久會話(CleanSession = 1)。
4.2 會話狀態
當 client 連接到 broker 時,可以使用持久連接或非持久連接,這是通過 CONNECT
消息中的 CleanSession 標誌來決定的(當 CleanSession = 0
時表示持久連接)。對於持久會話,broker 會存儲會話狀態;而對於非持久會話,broker 不會存儲 client 的任何內容。會話狀態主要包含以下內容:
4.2.1 客戶端存儲的會話狀態
- 已經發送 broker 但沒有收到確認的 QoS 1 和 QoS 2
PUBLISH
消息; - 從 broker 接收但還沒有收到確認的 QoS 2
PUBLISH
消息。
4.2.2 服務端存儲的會話狀態
- 客戶端的訂閱;
- 已經發送到 client 的但沒有得到確認的 QoS 1 和 QoS 2
PUBLISH
消息; - 從客戶端接收但還沒有確認的 QoS 2
PUBLISH
消息; - 等待發送到 client 的 QoS 1 和 QoS 2
PUBLISH
消息; - (可選項)等待發送到客戶端的 QoS 0
PUBLISH
消息。
提示: 保留消息不屬於會話狀態,在會話結束時不會被刪除,broker 應該一直存儲保留消息直到被 client 刪除。
4.3 QoS 發布服務品質等級
- QoS 0(默認): 最多發一次(不保證消息交付)
- QoS 1: 最少發一次(保證消息交付,但可能出現重複)
- QoS 2: 正好發一次(保證沒有重複的消息交付)
QoS 0 等級的 PUBLISH
消息的交付能力完全依賴於底層傳輸層,QoS 1 和 QoS 2 等級開始在應用層提高 PUBLISH
消息的交付能力。當消息丟失時,發送端會重新發送早前嘗試發送過的 PUBLISH
消息(DUP = 1),接收者收到消息也會發送確認響應消息。
4.3.1 QoS 0 · 最多發一次
在 QoS 0 的等級的 PUBLISH
消息中不包含包唯一標識。發送者不考慮消息交付結果,接收者也不發送響應。接收者最多只能收到一次消息,也有可能一次也收不到。
4.3.2 OoS 1 · 最少發一次
在 QoS 1 等級的 PUBLISH
消息中包含包唯一標識,發送方會一直將該消息當作 「未確認」 的消息,直到收到對應的 PUBACK
確認消息。具體消息流如下:
提示: 實際的消息傳遞是在 client 和 broker 之間進行的,這 4 個步驟是簡化為發送方和接收方之間的消息傳遞。
- 1、發送方存儲應用消息;
- 2、發送方發送
PUBLISH
(QoS = 1, DUP = 0,)消息; - 3、接收方收到
PUBLISH
消息,並響應PUBACK
()確認消息; - 4、發送方收到
PUBACK
消息,並刪除存儲的應用消息。
4.3.3 QoS2 · 正好發一次
QoS 2 是最高的服務品質,保證消息不會丟失也不會重複,缺點是會增加開銷。在 QoS 2 等級的 PUBLISH
消息中包含包唯一標識,發送者會一直將該消息當作 「未確認」 的消息,知道收到對應的 PUBCOMP
確認消息。
- 1、發送方存儲消息;
- 2、發送方發送
PUBLISH
(QoS = 2, DUP = 0,)消息; - 3、接收方收到
PUBLISH
消息,並存儲消息; - 4、接收方響應
PUBREC
()消息; - 5、發送方收到
PUBREC
消息,並發送PUBREL
()消息; - 6、接收方向上層應用通知消息;
- 7、接收方響應
PUBCOMP
()消息; - 8、發送方收到
PUBCOMP
消息,並刪除存儲的應用消息。
4.4 RETAIN 保留消息
當 client 發布某個主題的消息時,broker 會將該消息分發給任何已訂閱該主題的 client,隨後這條消息會從 broker 上刪除。可以設置 RETAIN
保留標誌設置該 PUBLISH 消息為保留消息,broker 會存儲該主題的最後一條保留消息,當新的 client 註冊訂閱時,並且匹配該消息主題時,該保留消息會發送給訂閱者。需要注意:broker 只會為每個主題保存最近一條保留消息,新收到的 RETAIN = 1 的消息會覆蓋原本那條保留消息;
持久會話 & 服務品質等級 & 保留消息都會影響新訂閱者是否接受消息,總結如下表:
- 對於保留消息(Retain Flag 為 Ture),新訂閱者總能收到最後一條保留消息(圖中綠色部分);
- 對於持久會話(Clean Session Flag 為 Flase)且訂閱者訂閱 OoS 大於等於 1,總能收到所有 OoS 大於等於 1 的消息(圖中黃色部分)。
4.5 消息重傳
標記 DUP = 1
的消息是被重複發送的消息,MQTT 消息重傳有 2 種場景:
- 1、PUBLISH / PUBREL 消息發送後,在規定時間內沒有收到確認應答消息,則重傳這個消息;
- 2、在使用持久會話時,client 重新連接後,broker 會自動重傳未確認的消息。
需要注意:DUP 標誌只對 OoS > 0 的消息有效,所有 QoS = 0 的消息 DUP 標誌必須設置為 0;
TCP 協議有報文重傳機制,為什麼 MQTT 協議還有消息重傳機制?
TCP 協議的報文重傳機制是對所有 TCP 報文有效的重傳機制,而 MQTT 協議的消息重傳機制只對一小部分消息有效,用於實現更可靠的消息交付保證。雖然 TCP 協議在一般情況下可以保證不丟包,但是這並不是絕對的,依然存在請求超時或者連接中斷等情況。而 MQTT 協議的 QoS 1 和 QoS 2 要求更可靠的交付能力,並且需要在客戶端重連後也能保證交付。因此,MQTT 協議也定義了一個消息重傳機制。
5. 總結
到這裡,關於 MQTT 協議的工作原理 & 協議消息格式 & 核心特性等內容就介紹完了。我知道你應該會對 MQTT 協議的實戰應用更加感興趣,下一篇文章里,我將帶你實現基於 MQTT 協議的 IM 服務,請關注。
參考資料
- MQTT 官網
- MQTT 協議中文版
- MQTT Protocol Guide —— Steve 著
- MQTT 協議是個啥?這篇文章告訴你! —— cxuan 著
- Android消息推送 MQTT 實戰 —— wildma 著
- Battle of The Protocols (HTTP vs. Websockets vs. MQTT) —— Ronak Singh 著
我是小彭,帶你構建 Android 知識體系。技術和職場問題,請關注公眾號 [彭旭銳]私信我提問。