MQTT-會話

木子七發表於2023-04-28
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 的訊息,不關心離線期間的訊息

相關文章