MQTT持久會話與Clean Session詳解。

MQTT持久會話

不穩定的網絡及有限的硬件資源是物聯網應用需要面對的兩大難題,MQTT客戶端與服務器的連接可能隨時會因為網絡波動及資源限制而異常斷開。

為了解決網絡連接斷開對通信造成的影響,MQTT協議提供了持久會話功能。

MQTT客戶端在發起到服務器的連接時,可以設置是否創建一個持久會話。

持久會話會保存一些重要的數據,以使會話能在多個網絡連接中繼續。

持久會話主要有以下三個作用:

  • 避免因網絡中斷導致需要反復訂閱帶來的額外開銷。
  • 避免錯過離線期間的消息。
  • 確保QoS 1和QoS 2的消息質量保證不被網絡中斷影響。

持久會話需要存儲哪些數據?

通過上文我們知道持久會話需要存儲一些重要的數據,以使會話能被恢復。

這些數據有的存儲在客戶端,有的則存儲在服務端。

客戶端中存儲的會話數據:

  • 已發送給服務端,但是還沒有完成確認的QoS 1與QoS 2消息。
  • 從服務端收到的,但是還沒有完成確認的QoS 2消息。

服務端中存儲的會話數據:

  • 會話是否存在,即使會話狀態其餘部分為空。
  • 已發送給客戶端,但是還沒有完成確認的QoS 1與QoS 2消息。
  • 等待傳輸給客戶端的QoS 0消息《可選》,QoS 1與QoS 2消息。
  • 從客戶端收到的,但是還沒有完成確認的QoS 2消息,遺囑消息和遺囑延時間隔。

MQTT Clean Session的使用

Clean Session是用來控制會話狀態生命周期的標志位,為true時表示創建一個新的會話,在客戶端斷開連接時,會話將自動銷毀。

為false時表示創建一個持久會話,在客戶端斷開連接後會話仍然保持,直到會話超時註銷。

注意:持久會話能被恢復的前提是客戶端使用固定的ClientID再次連接,如果ClientID是動態的,那麼連接成功後將會創建一個新的持久會話。

如下為開源MQTT服務器EMQX的Dashboard,可以看到圖中的連接雖然是斷開狀態,但是因為它是持久會話,所以仍然能被查看到,並且可以在Dashboard中手動清除該會話。

MQTT持久會話與Clean Session詳解。

同時,EMQX也支持在Dashboard中設置Session相關參數。

MQTT 3.1.1沒有規定持久會話應該在什麼時候過期,如果僅從協議層面理解的話,這個持久會話應該永久存在。

但在實際場景中這並不現實,因為它會非常占用服務端的資源,所以服務端通常不會完全遵循協議來實現,而是向用戶提供一個全局配置來限制會話的過期時間。

比如EMQ提供的免費的公共MQTT服務器設置的會話過期時間為5分鐘,最大消息數為1000條,且不保存QoS0消息。

接下來我們使用開源的跨平臺MQTT 5.0桌面客戶端工具-MQTT X演示Clean Session的使用。

打開MQTT X後如下所示,點擊New Connection按鈕創建一個MQTT連接。

創建一個名為MQTT_V3的連接,Clean Session為關閉狀態《即為false》,MQTT版本選擇3.1.1,然後點擊右上角的Connect按鈕。

連接的服務器默認為EMQ提供的免費的公共MQTT服務器。

連接成功後訂閱clean_session_false主題,且QoS設置為1。

訂閱成功後,點擊右上角的斷開連接按鈕。

然後,創建一個名為MQTT_V3_Publish的連接,MQTT版本同樣設置為3.1.1,連接成功後向clean_session_false主題發佈兩條QoS 1消息。

然後選中MQTT_V3連接,點擊連接按鈕連接至服務器,將會成功接收到兩條離線期間的消息。

MQTT 5.0中的會話改進

MQTT 5.0中將Clean Session拆分成了Clean Start與Session Expiry Interval。

Clean Start用於指定連接時是創建一個全新的會話還是嘗試復用一個已存在的會話,Session Expiry Interval用於指定網絡連接斷開後會話的過期時間。

Clean Start為true時表示必須丟棄任何已存在的會話,並創建一個全新的會話;為false時表示必須使用與Client ID關聯的會話來恢復與客戶端的通信《除非會話不存在》。

Session Expiry Interval解決了MQTT 3.1.1中持久會話永久存在造成的服務器資源浪費問題。

設置為0或未設置,表示斷開連接時會話即到期;設置為大於0的數值,則表示會話在網絡連接關閉後會保持多少秒;設置為0xFFFFFFFF表示會話永遠不會過期。

關於MQTT會話的Q&A

當會話結束後,保留消息還存在麼?

MQTT保留消息不是會話狀態的一部分,它們不會在會話結束時被刪除。

客戶端如何知道當前會話是被恢復的會話?

MQTT協議從v3.1.1開始,就為CONNACK報文設計了Session Present字段。

當服務器返回的該字段值為1時,表示當前連接將會復用服務器保存的會話。

客戶端可通過該字段值決定在連接成功後是否需要重新訂閱。

  • 不能使用動態Client ID,需要保證客戶端每次連接的Client ID都是固定的。
  • 根據服務器性能、網絡狀況、客戶端類型等合理評估會話過期時間。

    設置過長會占用更多的服務端資源,設置過短會導致未重連成功會話就失效。

  • 當客戶端確定不再需要會話時,可使用Clean Session為true進行重連,重連成功後再斷開連接。

    如果是MQTT 5.0則可在斷開連接時直接設置Session Expiry Interval為0,表示連接斷開後會話即失效。

結語

至此,我們完成了對MQTT持久會話的介紹,並通過桌面客戶端演示了Clean Session的使用。

大家可參考本文借助MQTT持久會話實現離線消息的接收及降低訂閱開銷。