參考 Reference
v3.1.1
其他資源
網站
測試工具
基本概念 Basic Conception
Session 會話
定義
- 定義:某個客戶端(由ClientID作為標識)和某個服務器之間的邏輯層面的通信
ClientID
- 只能包含這些 大寫字母,小寫字母 和 數字(0-9a-zA-Z),23個字符以內
- 如果 ClientID 在多次 TCP連接中保持一致,客戶端和服務器端會保留會話信息(Session)
- 同一時間內 Server 和同一個 ClientID 只能保持一個 TCP 連接,再次連接會踢掉前一個
CleanSession 標記
- 0 —— 開啟會話重用機制。網絡斷開重連后,恢復之前的Session信息。需要客戶端和服務器有相關Session持久化機制。
- 1 —— 關閉會話重用機制。每次Connect都是一個新Session,會話僅持續和網絡連接同樣長的時間。
客戶端 Session
- 已經發送給服務端,但是還沒有完成確認的 QoS 1 和 QoS 2 級別的消息
- 已從服務端接收,但是還沒有完成確認的 QoS 2 級別的消息
服務器端 Session
- 會話是否存在,即使會話狀態的其它部分都是空 (SessionFlag)
- 客戶端的訂閱信息 (ClientSubcription)
- 已經發送給客戶端,但是還沒有完成確認的 QoS 1 和 QoS 2 級別的消息
- 即將傳輸給客戶端的 QoS 1 和 QoS 2 級別的消息
- 已從客戶端接收,但是還沒有完成確認的 QoS 2 級別的消息
- (可選)准備發送給客戶端的 QoS 0 級別的消息
長連接維護與管理
Keep Alive 心跳
- 目的是保持長連接的可靠性,以及雙方對彼此是否在線的確認。
- 客戶端在Connect的時候設置 Keep Alive 時長。如果服務端在 1.5 * KeepAlive 時間內沒有收到客戶端的報文,它必須斷開客戶端的網絡連接
- Keep Alive 的值由具體應用指定,一般是幾分鍾。允許的最大值是 18 小時 12 分 15 秒
Will 遺囑
- 遺囑消息(Will Message)存儲在服務端,當網絡連接關閉時,服務端必須發布這個遺囑消息,所以被形象地稱之為遺囑,可用於通知異常斷線。
- 客戶端發送 DISCONNECT 關閉鏈接,遺囑失效並刪除
- 客戶端在保持連接(Keep Alive)的時間內未能通訊
- 客戶端沒有先發送 DISCONNECT 報文直接關閉了網絡連接
- 0 -- 關閉遺囑功能,Will QoS 和 Will Retain 必須為 0
- 1 -- 開啟遺囑功能,需要設置 Will Retain 和 Will QoS
消息基本概念
報文標識 Packet Identifier
- 存在報文的可變報頭部分,非零兩個字節整數 (0-65535]
- 一個流程中重復:這些報文包含 PacketID,而且在一次通信流程內保持一致:
- PUBLISH(QoS>0 時),PUBACK,PUBREC,PUBREL,PUBCOMP
- 新的不重復:客戶端每次發送一個新的這些類型的報文時都必須分配一個當前 未使用的PacketID
- 當客戶端處理完這個報文對應的確認后,這個報文標識符就釋放可重用。
- 獨立維護:客戶端和服務端彼此獨立地分配報文標識符。因此,客戶端服務端組合使用相同的報文標識符可以實現 並發 的消息交換。可能出現一下情況,並不算異常:
Payload 有效載荷,消息體
- Publish 的 Payload 允許為空。在很多場合下,代表將持久消息(或者遺囑消息)清空。
Retain 持久消息(粘性消息)
- RETAIN 標記:每個Publish消息都需要指定的標記
- 0 —— 服務端不能存儲這個消息,也不能移除或替換任何 現存的保留消息
- 1 —— 服務端必須存儲這個應用消息和它的QoS等級,以便它可以被分發給未來的訂閱者
- 每個Topic只會保留最多一個 Retain 持久消息
- 客戶端訂閱帶有持久消息的Topic,會立即受到這條消息
- 服務器可以選擇丟棄持久消息,比如內存或者存儲吃緊的時候
- 如果客戶端想要刪除某個Topic 上面的持久消息,可以向這個Topic發送一個Payload為空的持久消息
QoS 服務等級(消息可靠性)
最多一次 At most Once(QoS == 0)
- 沒有回復,不需要存儲。有可能丟失(網絡異常斷開,業務層繁忙或者錯誤)
至少一次 At least Once(QoS == 1 )
- 發送者S 發送前需要做持久化存儲,接受者R 不需要持久化存儲
- 如果 發送者S 沒有收到 接收者R 的回復 PUBACK,過一段時間 發送者S 會重新發送,DUP標記為1(在同一Session內)。
- 接受者R 發送 PUBACK 后,不需要知道對方是否收到,馬上把消息交給上層業務。如果此時網絡異常,會導致發送者重發。這樣接受者收到多個消息(所以叫至少一次)。
有且僅有一次 Exactly Once(QoS == 2 )
- 發送者S 發送 PUBLISH 前,需要做持久化存儲。接受者R 回復PUBREC 后,也需要做持久化存儲
- 如果 發送者S 沒有收到 接收者R 的回復 PUBREC,過一段時間 發送者S 會重新發送,DUP標記為1(在同一Session內)。
- 如果 接受者R 沒有收到 發送者S 的回復 PUBREL,過一段時間 接受者R 會重新發送PUBREC。
- 發送者S 收到 PUBREC后,刪除持久化消息,但是要保存 PacketID
- 接收者R 受到 PUBREL后,刪除持久化PUBREC。然后將消息發給上層,同時回復 PUBCOMP。
- 發送者S 收到 PUBCOMP 后,刪除 PacketID,通信完美結束。
- 這套流程可以 嚴格保證 一個包不管在什么情況下 接收者R 只收到一次 。
重傳標記 DUP 與重傳機制 (QoS > 0)
- 如果客戶端或者服務器發送了一個 Publish 消息,一段時間內沒收到 PublishAck 回復,則認為消息丟失,進行重傳。
- 在一個Session內,進行重傳的時候,頭部的 DUP 重傳標志 設置為1。
- 客戶端有可能收到 DUP == 0 的重傳包(Payload相同,PacketID不同)。因為可能因為網絡問題,下次重傳時間較久,Session已經釋放,PacketID 已經變更。
- 客戶端發給服務器的和服務器轉發給別的客戶端的 Publish 消息,DUP 重傳標志不會傳遞
- 接收者收到一個 DUP 標志為 1 的控制報文時,並不能保證之前收到過相同的報文
消息重傳順序
- 重發任何之前的 PUBLISH 報文時,必須按原始 PUBLISH 報文的發送順序重發 (適用於QoS 1 和 QoS 2 消息)
- 必須按照對應的 PUBLISH 報文的順序發送 PUBACK 報文 (QoS 1 消息)
- 必須按照對應的 PUBLISH 報文的順序發送 PUBREC 報文 (QoS 2 消息)
- 必須按照對應的 PUBREC 報文的順序發送 PUBREL 報文 (QoS 2 消息)
- QoS == 1 時,雖然是PUBLISH有序的,但是可能會重復。例如,發布者按順序 1,2,3,4 發送消息,訂閱者收到的順序可能是 1,2,3,2,3,4。
- QoS == 1 時,如果限制 傳輸窗口 (in-flight window) ==1,即同一時刻只有一個包在傳輸,就可以保證亂序。例如,訂閱者收到的順序可能是 1,2,3,3,4,而不是 1,2,3,2,3,4
話題 與訂閱機制 Topic & Subcribe
Topic 話題 和 TopicFilter 話題過濾器
- UTF-8 編碼字符串,不能超過 65535 字節。層級數量沒有限制
- 不能包含任何的下文中提到的特殊符號(/、+、#),必須至少包含一個字符
- 區分大小寫,可以包含空格,不能包含空字符 (Unicode U+0000)
- 在收部或尾部增加 斜杠 “/”,會產生不同的Topic和TopicFilter。舉例:
- 只包含斜杠 “/” 的 Topic 或 TopicFilter 是合法的
TopicFilter中的特殊符號
- 主題層級分隔符可以出現在 Topic 或 TopicFilter 的任何位置
- 特例:相鄰的主題層次分隔符表示一個零長度的主題層級
- 只能用於單個主題層級匹配的通配符。例如,“a/b/+” 匹配 “a/b/c1” 和 “a/b/c2” ,但是不匹配 “a/b/c/d”
- 可以匹配 任意層級,包括第一個和最后一個層級。例如,“+” 是有效的,“sport/+/player1” 也是有效的。
- 可以在多個層級中使用它,也可以和多層通配符一起使用。 例如,“+/tennis/#” 是有效的。
- 只能匹配本級不能匹配上級。例如,“sport/+” 不匹配 “sport” 但是卻匹配“sport/”,“/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。
- 匹配包含本身的層級和子層級。例如 “a/b/c/#" 可以匹配 “a/b/c”、“a/b/c/d” 和 “a/b/c/d/e”
- 必須是最后的結尾。例如“sport/tennis/#/ranking”是無效的
- “#”是有效的,會收到所有的應用消息。 (服務器端應將此類 TopicFilter禁掉 )
以$開頭的,服務器保留
- 服務端不能將 $ 字符開頭的 Topic 匹配通配符 (#或+) 開頭的 TopicFilter
- 服務端應該阻止客戶端使用這種 Topic 與其它客戶端交換消息。服務端實現可以將 $ 開頭的主題名用作其他目的。
- $SYS/ 被廣泛用作包含服務器特定信息或控制接口的主題的前綴
- 客戶端不特意訂閱 $開頭的 Topic,就不會收到對應的消息
- 訂閱 “#” 的客戶端不會收到任何發布到以 “$” 開頭主題的消息
- 訂閱 “+/A/B” 的客戶端不會收到任何發布到 “$SYS/A/B” 的消息
- 訂閱 “$SYS/#” 的客戶端會收到發布到以 “$SYS/” 開頭主題的消息
- 訂閱 “$SYS/A/+” 的客戶端會收到發布到 “$SYS/A/B” 主題的消息
- 如果客戶端想同時接受以 “$SYS/” 開頭主題的消息和不以 $ 開頭主題的消息,它需要同時 訂閱 “#” 和 “$SYS/#”
訂閱 Subscribe 與 QoS降級
- 一個Subsribe請求 可訂閱多個 Topic(節省帶寬,多訂閱盡量用一次請求)。取消訂閱也同理
- 每一個訂閱需要指定一個QoS,指定了客戶端接收消息所允許的最大QoS級別。但是服務器端最終授權返回的QoS可能會小於等於客戶端請求的QoS
- 對於高於QoS的消息(比如說訂閱的QoS限制到1,消息的QoS指定到2),那么客戶端會收到一個QoS降低為指定的 限制QoS 的消息(消息的QoS降為1,不保證只收到一次)
- 訂閱關系可以被覆蓋,以TopicFilter為標識。如果后面訂閱一個相同的TopicFilter,但是指定的QoS不同,則以后面的為准,QoS升高后,重發相應等級的 Retain 消息
安全傳輸與鑒權認證 Security & Certification
傳輸層
- 可以采用 TCP、SSL/TLS [RFC5246] 、WebSocket 作為傳輸層。UDP不可以,因為不保證可靠傳輸與有序傳輸。
- 服務器端返回的數據極有可能出現 粘包 的情況。客戶端經常會在連接建立之后,連續調用多個訂閱,這樣服務器端就會回復多個訂閱ACK包,同時還有各個Topic上的持久消息,一般粘成一個TCP包返回過來
- 8883:over SSL/TLS,單向認證(強烈建議)
潛在的風險與應對機制
- 客戶端和服務端的靜態數據可以被訪問(比如客戶端Root導致數據泄露、服務器被拖庫)
- 協議規定的行為可能有副作用 (如計時器攻擊 “timing attacks”)
- 通信可能會被攔截、修改、重定向或者泄露(抓包、中間人)
客戶端身份驗證與授權 (Authentication & Authorization of Client)
- 用戶名+密碼驗證:Connect 登錄的時候,傳入 UserName 和 Password
- 用戶名(UserName Flag)標記設置為1,才可以穿入
- 外部驗證:LDAP、OAuth 或者 操作系統的認證機制
- 應用層:客戶端通過應用消息給服務端發送憑證用於身份驗證。
- 授權:基於客戶端提供的信息如用戶名、客戶端標識符(ClientId)、客戶端的主機名或 IP 地址,或者身份認證的結果,服務端可以限制對某些服務端資源的訪問
服務端身份驗證 (Authentication of Server by Client)
- MQTT 協議不是雙向信任的,它沒有提供客戶端驗證服務端身份的機制
- TLS:客戶端可以使用服務端發送的SSL證書驗證服務端的身份
- 應用層:可以通過服務端給客戶端發送憑證用於身份驗證的應用層消息
- VPN:在客戶端和服務端之間使用虛擬專用網(VPN)可以確保客戶端連接的是預期的服務器。
控制報文和 Payload 的完整性(Integrity)
- TLS:提供了對網絡傳輸的數據做完整性校驗的哈希算法
- 應用層:可以在應用消息中單獨包含哈希值。這樣做可以為 PUBLISH 控制報文的網絡傳輸和靜態數據提供內容的完整性檢查
- VPN:在客戶端和服務端之間使用虛擬專用網(VPN)連接可以在 VPN 覆蓋的網絡段提供數據完整性檢查
控制報文和 Payload 的保密性(Privacy)
- 輕量級加密:AES or DES,可適用於低端設備
- 應用層:可以單獨加密 Payload 內容。這可以提供 Payload 傳輸途中和靜態數據的私密性。但不能給應用消息的其它屬性如 Topic 加密
- 靜態數據加密:客戶端和服務端實現可以加密存儲靜態數據,例如可以將應用消息作為會話的一部分存儲
- VPN:在客戶端和服務端之間使用虛擬專用網(VPN)連接可以在 VPN 覆蓋的網絡段保證數據的私密性
異常行為的檢測
- 服務端實現可以監視客戶端的行為,檢測潛在的安全風險。例如:
- 發現違反安全規則的行為,服務端實現可以斷開客戶端連接
- 可以基於 IP地址 或 ClientID 實現一個 動態黑名單列表
- 可以使用網絡層面的控制,實現基於 IP 地址或其它信息的 速率限制 或黑名單
最佳實踐 Best Practice
客戶端 Client
選型
服務器端 Server
Broker選型
Benchmark
分布式部署 Cluster