MQTT的訂閱釋出機制,解耦了訊息的傳送方和接收方,這使我們沒有辦法獲取對端的狀態,為了解決該問題,MQTT提供了遺囑訊息,為意外斷線的使用者端提供了對外發出通知的能力
使用遺囑訊息,使用者端需要在連線時,也就是connect報文中指定遺囑訊息,除了正常CONNECT報文欄位,需要為遺囑訊息提供以下欄位
Will Topic #遺囑訊息主題
Will QoS # 遺囑訊息級別
Will Retain # 將遺囑訊息設定為保留訊息
'''
遺囑訊息一旦釋出,就會在伺服器端的對談狀態中刪除,不能多次消費,我們不能保證遺囑訊息發出的時候訂閱端是否線上,為了避免錯過遺囑訊息,可以使用Will Retain = True 欄位將遺囑訊息設定為保留訊息,這樣訂閱了該主題的使用者端不管什麼時候上線,都可以收到另外一方的離線通知
'''
Will Properties # 遺囑訊息屬性 ↓
Will Delay Interval #遺囑訊息的屬性↑, 設定遺囑訊息的延遲傳送時間
'''
在MQTT 5.0中,可以使用 Will Delay Interval 設定延遲傳送遺囑訊息,單位是S,如果使用者端及時恢復,那麼遺囑訊息的發生倒計時就會終止,可以避免使用者端在短暫離線後恢復,可以繼續服務時但是遺囑訊息已經發出的情況,和保留訊息不同的是,遺囑訊息是對談狀態的一部分,沒有辦法存在比對談更長的時間,如果遺囑延遲時間大於對談過期時間,對談結束的時候遺囑會立即釋出
'''
Will Payload # 遺囑訊息內容
在使用者端連線成功後,遺囑訊息就會儲存在伺服器端中,一旦使用者端連線異常斷開,伺服器端就會把遺囑訊息傳送給對應的訂閱者,如果使用者端是正常斷開,遺囑訊息則不會發布,在MQTT中,使用者端的意外斷開可以分為以下幾種情況
如果不考慮持久對談的因素,那麼MQTT訂閱只有在使用者端連線之後才能建立,所以伺服器端不能提前預知某個主題會被哪些伺服器端訂閱或者某個使用者端會訂閱哪些主題,所以當訊息到達伺服器端之後,伺服器端只會把訊息分發給當前已經存在的訂閱者,分發完成訊息就會從伺服器端中刪除,如果當前沒有任何訂閱者,訊息就會立即丟棄,如果訂閱端在在這之後上線的,就會錯過這個訊息,為了解決該場景的問題,MQTT提供了保留訊息
我們可以在傳送PUBLISH的時候把Retain設定為1或者True,表示當前訊息是保留訊息,保留訊息進入伺服器端,會像普通訊息一樣轉發給當前的訂閱者,還會被保留在MQTT的伺服器端中,每當有新的訂閱建立,MQTT伺服器端都會檢索是否存在與這個訂閱匹配的保留訊息,然後把匹配的保留訊息下發給訂閱者,由於訂閱的時候可以使用主題萬用字元,所以可能匹配到多個保留訊息,這些訊息將依次下發給訂閱者
通過保留訊息,我們可以使訂閱者上線後立即獲得資料更新,不必等待新一次的訊息,每個主題最多可以儲存一條保留訊息,如果主題下的保留訊息已經存在,那麼新到達的保留訊息就會替換原來的保留訊息,保留訊息會一直儲存在伺服器端中,由於他不屬於對談狀態的一部分,所以即便釋出端對談過期,也不會影響保留訊息儲存,如果想要清空這個主題的保留訊息,可以通過傳送一個payload為空的保留訊息來實現,這個為空的保留訊息會合其他保留訊息一樣轉發給訂閱者,區別是在於不會被伺服器端儲存,使用這種方式的話,我們需要確保訂閱端不會把為空的訊息視為一個錯誤
需要注意的是,QoS 0 可能丟訊息的特性,可能會導致保留訊息刪除不成功,QoS 1 可能重複到達的特性,保留訊息又可能多次刪除,如果不希望出現刪除失敗或者多次刪除的情況,可以使用QoS 2 來發布 payload為空的保留訊息
如果擔心一條保留訊息失去了時效性,還長時間保留在伺服器端中導致被後來的訂閱者消費的話,MQTT 5.0版本可以為保留訊息設定過期時間,PUBLISH的 Message Expiry Interval 屬性,單位是秒
預設情況下,當保留訊息當成普通訊息向訂閱者轉發的時候,保留訊息中的retain標識會被清除也就是設定為0,只有當新的訂閱建立的時候,傳送保留訊息的retain會設定為1,表示這是一個保留訊息
針對上述情況多個伺服器端橋接的時候,會衍生一個問題,比如伺服器端A向伺服器端B訂閱了主題,當伺服器端B收到一個保留訊息向伺服器端A轉發的時候清除retain標識,導致伺服器端A收到該保留訊息後只會轉發給訂閱者,不會儲存保留訊息,在MQTT 5.0中,提供了 Retain As Published 的訂閱選項,如果該選項設定為0,就是預設的邏輯,如果設定為1,那麼保留訊息當做普通訊息轉發的時候,Retain標識不會被清除,依然是1
還有一個選項會影響保留訊息的行為,在某些場景下,雖然使用者端複用了上一次的對談,但是無法確定上一次對談中是否成功訂閱了某個主題,所以只能再次訂閱,如果訂閱已經存在,其實伺服器端已經給使用者端快取了離線期間的訊息,這種情況下,使用者端在重連後,其實並不需要獲取保留訊息,但是現在只要有訂閱建立,訂閱匹配就會下發保留訊息,為了解決這個問題,在MQTT 5.0中,提供了 Retain Handling的訂閱選項