假如有以下場景,使用者端A傳送訊息到伺服器端,伺服器端轉發給使用者端B,如果這個時候伺服器端和使用者端B的網路連線斷開,那麼就無法保證訊息到達,並且使用者端A不知道B連線斷開,還會繼續傳送訊息,訊息到達伺服器端之後會因為沒有訂閱者被丟棄,後面如果使用者端B和伺服器端重新進行連線,但是還需要重新訂閱進行正常通訊
根據以上的場景,問題的關鍵在於伺服器端和使用者端中已經傳送但是沒有完全確認的訊息和等待傳送的訊息以及使用者端訂閱資訊等,這些重要的資訊不應該隨著斷開而訊息,我們應該把這些重要資訊儲存下來,並獨立於連線存在,以此引出了對談的作用和意義
對談是MQTT通訊的基礎,當用戶端和伺服器端建立MQTT連線,他們就建立了一個有狀態的對談,後續訊息的收發都會在這個對談上進行,訊息的傳送進度,待傳送的訊息列表,都屬於對談狀態的一部分
對談的生命週期根據連線和斷開分為以下幾種
我們可以讓對談僅持續和網路連線一樣長的時間,這表示對談狀態將在網路斷開的時候被丟棄,下次連線時,必須建立一個新的對談
可以使對談跨越多個網路連線存在,這就要去使用者端和伺服器端斷開連線的時候,必須儲存各自的對談狀態,比如傳送沒有完全確認的訊息等,以便下次連線時通過對談狀態恢復通訊,我們把這個對談稱為持久對談
為了確保雙方能夠從正確的對談中恢復通訊,伺服器端和和使用者端需要把Client ID 唯一標誌進行關聯
MQTT為伺服器端和使用者端分別定義了他們需要儲存的對談狀態
伺服器端儲存的對談狀態:
使用者端的訂閱資訊:使用者端只要在對談過期前重連,就不用再重新訂閱,因為訂閱仍然存在,即便使用者端離線狀態,伺服器端也可以給使用者端快取後續到達的訊息
已傳送但還未完成確認的 QoS 1和QoS 2的訊息以及等待傳送的QoS 0、1、2的訊息:既包含了上一次連線沒來得及下發的訊息,也包含了離線期間新到達的訊息, QoS 0 的訊息,協定沒有強制要求,可以儲存也可以補儲存,可以提供選項自定義是否需要儲存
從使用者端收到的還沒有完成確認的QoS 2 訊息:以便重新連線後QoS2的傳輸流程能夠正常恢復
遺囑訊息和遺囑延遲間隔:這裡是協定mqtt3.1.1和5.0版本的不同,如果是3.1.1中斷開連線遺囑訊息會立即下發,則不需要伺服器端儲存
對談是否存在:如果斷開連線的時間太長,對談可能過期,也可能因為其他故障導致的對談丟失,所以需要伺服器端儲存對談是否存在的資訊,使用者端連線伺服器端時,伺服器端會使用連線報文中的 Client ID 來尋找對應的對談狀態,然後通過響應報文中的 Session Persent欄位來告訴使用者端是否複用了之前的對談,以便兩端在一個正確的基礎上進行通訊
使用者端需要儲存的對談狀態:
Clean Start欄位
用於表示是否在連線時複用已經存在的對談
Session Expiry Interval欄位
指定對談在連線斷開後能夠保留的最長時間,如果過,期時間內使用者端沒有重新連線,伺服器端則會丟棄對應的對談狀態
每個使用者端都可以獨立設定自己的Session Expiry Interval ,MQTT允許使用者端在斷開連線(DISCONNECT)的時候 重新設定過期時間,比如延長對談時間或者直接為0取消持久對談等等
3.1.1協定中只有一個Clean Session欄位
Clean Session欄位
3.1.1協定的的靈活性不如5.0,在3.1.1協定中,不能為每個使用者端設定單獨的對談時間 也不能斷開時重新設定過期時間,導致不需要對談保留很久的使用者端,對談資訊也回被伺服器端長時間儲存,也造成一定程度上伺服器端資源的浪費