MQTT-對談

2023-04-28 15:00:41
MQTT對談

為什麼需要對談

​ 假如有以下場景,使用者端A傳送訊息到伺服器端,伺服器端轉發給使用者端B,如果這個時候伺服器端和使用者端B的網路連線斷開,那麼就無法保證訊息到達,並且使用者端A不知道B連線斷開,還會繼續傳送訊息,訊息到達伺服器端之後會因為沒有訂閱者被丟棄,後面如果使用者端B和伺服器端重新進行連線,但是還需要重新訂閱進行正常通訊

​ 根據以上的場景,問題的關鍵在於伺服器端和使用者端中已經傳送但是沒有完全確認的訊息和等待傳送的訊息以及使用者端訂閱資訊等,這些重要的資訊不應該隨著斷開而訊息,我們應該把這些重要資訊儲存下來,並獨立於連線存在,以此引出了對談的作用和意義

什麼是對談

​ 對談是MQTT通訊的基礎,當用戶端和伺服器端建立MQTT連線,他們就建立了一個有狀態的對談,後續訊息的收發都會在這個對談上進行,訊息的傳送進度,待傳送的訊息列表,都屬於對談狀態的一部分

對談的生命週期

對談的生命週期根據連線和斷開分為以下幾種

  • 對談生命週期與網路連線相同

我們可以讓對談僅持續和網路連線一樣長的時間,這表示對談狀態將在網路斷開的時候被丟棄,下次連線時,必須建立一個新的對談

  • 對談生命週期長於網路連線

可以使對談跨越多個網路連線存在,這就要去使用者端和伺服器端斷開連線的時候,必須儲存各自的對談狀態,比如傳送沒有完全確認的訊息等,以便下次連線時通過對談狀態恢復通訊,我們把這個對談稱為持久對談

恢復對談

為了確保雙方能夠從正確的對談中恢復通訊,伺服器端和和使用者端需要把Client ID 唯一標誌進行關聯

MQTT為伺服器端和使用者端分別定義了他們需要儲存的對談狀態

伺服器端儲存的對談狀態:

  1. 使用者端的訂閱資訊:使用者端只要在對談過期前重連,就不用再重新訂閱,因為訂閱仍然存在,即便使用者端離線狀態,伺服器端也可以給使用者端快取後續到達的訊息

  2. 已傳送但還未完成確認的 QoS 1和QoS 2的訊息以及等待傳送的QoS 0、1、2的訊息:既包含了上一次連線沒來得及下發的訊息,也包含了離線期間新到達的訊息, QoS 0 的訊息,協定沒有強制要求,可以儲存也可以補儲存,可以提供選項自定義是否需要儲存

  3. 從使用者端收到的還沒有完成確認的QoS 2 訊息:以便重新連線後QoS2的傳輸流程能夠正常恢復

  4. 遺囑訊息和遺囑延遲間隔:這裡是協定mqtt3.1.1和5.0版本的不同,如果是3.1.1中斷開連線遺囑訊息會立即下發,則不需要伺服器端儲存

  5. 對談是否存在:如果斷開連線的時間太長,對談可能過期,也可能因為其他故障導致的對談丟失,所以需要伺服器端儲存對談是否存在的資訊,使用者端連線伺服器端時,伺服器端會使用連線報文中的 Client ID 來尋找對應的對談狀態,然後通過響應報文中的 Session Persent欄位來告訴使用者端是否複用了之前的對談,以便兩端在一個正確的基礎上進行通訊

使用者端需要儲存的對談狀態:

  • 所有傳送給伺服器端但是還沒有完成確認的QoS1和2訊息以及從伺服器端收到的沒有確認的QoS2的訊息,原理和伺服器端狀基本類似,不再贅述
不同協定版本中的對談欄位

MQTT 5.0對談欄位

Clean Start欄位

用於表示是否在連線時複用已經存在的對談

  • Clean Start = 0 ,表示嘗試從已存在的對談中恢復通訊,使用者端連線時,如果伺服器端中存在與指定client id關聯的對談,伺服器端就必須基於這個對談來恢復通訊,如果不存在對應的對談,伺服器端必須建立一個全新的對談
  • Clean Start = 1,不從對談中恢復通訊,即便使用者端和伺服器端連線時存在相應的對談,伺服器端也必須丟棄對談建立一個全新的對談

Session Expiry Interval欄位

指定對談在連線斷開後能夠保留的最長時間,如果過,期時間內使用者端沒有重新連線,伺服器端則會丟棄對應的對談狀態

  • Session Expiry Interval = 0,生命週期與網路連線相同,對談將在網路連線斷開的時候立即結束
  • Session Expiry Interval > 0,如果值大於0,則表示對談將在連線斷開後多少秒後過期
  • Session Expiry Interval = 0xFFFFFFFF,表示對談永不過期

每個使用者端都可以獨立設定自己的Session Expiry Interval ,MQTT允許使用者端在斷開連線(DISCONNECT)的時候 重新設定過期時間,比如延長對談時間或者直接為0取消持久對談等等

MQTT3.1.1 對談欄位

3.1.1協定中只有一個Clean Session欄位

Clean Session欄位

  • Clean Session = 0,等同於5.0中 Clean Start = 0 、Session Expiry Interval = 0xFFFFFFFF
  • Clean Session = 1,等同於5.0中 Clean Start = 1 、Session Expiry Interval = 0

3.1.1協定的的靈活性不如5.0,在3.1.1協定中,不能為每個使用者端設定單獨的對談時間 也不能斷開時重新設定過期時間,導致不需要對談保留很久的使用者端,對談資訊也回被伺服器端長時間儲存,也造成一定程度上伺服器端資源的浪費

如何選擇對談的生命週期

選擇持久對談的場景
  • 不希望錯誤離線期間的訊息
  • 不希望QoS 1 和QoS 2訊息丟失
  • 不希望每次連線都重新建立訂閱
  • 裝置定期休眠,不希望長時間維護連線
選擇非持久對談的場景
  • 只對外發布 QoS 0的訊息,不會接收任何訊息
  • 只訂閱QoS 0 的訊息,不關心離線期間的訊息