MQTT 主題與通配符Wildcards :初學者指南
https://www.emqx.com/en/blog/advanced-features-of-mqtt-topics
目錄
- 主題
- MQTT 通配符
- 以 $ 開頭的主題
- 不同場景的主題
- MQTT 主題常見問題解答
MQTT 主題是MQTT 協定中用於識別和路由訊息的字串。它是 MQTT 發布者和訂閱者之間溝通的關鍵元素。在MQTT發布/訂閱模式中,發布者將訊息傳送到特定主題,而訂閱者可以訂閱這些主題來接收訊息。
與其他訊息系統(例如 Kafka 和 Pulsar)中的主題相比,MQTT 主題不需要事先建立。客戶端訂閱或發佈時會自動建立主題,不需要刪除主題。
以下是一個簡單的 MQTT 發布和訂閱流程。如果APP 1訂閱了該sensor/2/temperature
主題,它將收到來自Sensor 2發佈到該主題的訊息。
主題
主題是 UTF-8 編碼的字串,它是 MQTT 協定中訊息路由的基礎。主題通常是分級的,並/
在等級之間以斜線分隔。這與 URL 路徑類似,例如:
chat/room/1 sensor/10/temperature sensor/+/temperature sensor/#
儘管允許,但通常不建議使用以 開頭或結尾的主題/
,例如/chat
或chat/
。
MQTT 通配符
MQTT 通配符是一種特殊類型的主題,只能用於訂閱,不能用於發布。客戶端可以訂閱通配符主題來接收來自多個匹配主題的訊息,從而無需單獨訂閱每個主題並減少開銷。MQTT 支援兩種類型的通配符:(+
單級)和#
(多層)。
單級通配符
+
(U+002B) 是僅符合一個主題層級的通配符。使用單級通配符時,單級通配符必須佔據整個級別,例如:
"+" is valid
"sensor/+" is valid
"sensor/+/temperature" is valid
"sensor+" is invalid (does not occupy an entire level)
如果用戶端訂閱了該主題sensor/+/temperature
,它將收到來自以下主題的訊息:
sensor/1/temperature
sensor/2/temperature
...
sensor/n/temperature
但它不會匹配以下主題:
sensor/temperature sensor/bedroom/1/temperature
多層通配符 Multi-level Wildcard
#
(U+0023) 是一個通配符,可符合主題中任意數量的等級。使用多層通配符時,它必須佔據整個級別,並且必須是主題的最後一個字符,例如:
"#" is valid, matches all topics
"sensor/#" is valid
"sensor/bedroom#" is invalid (+ or # are only used as a wildcard level)
"sensor/#/temperature" is invalid (# must be the last level)
以 $ 開頭的主題
系統主題
以 開頭的主題$SYS/
是系統主題,主要用於取得 MQTT 伺服器的運作狀態、統計資料、客戶端上下線事件等元資料。主題在 MQTT 規格中沒有定義$SYS/
。然而,大多數MQTT 代理都會遵循此建議。
例如,EMQX支援透過以下主題獲取集群狀態。
主題 | 描述 |
---|---|
$SYS/brokers | EMQX 叢集節點列表 |
$SYS/brokers/${node}/version | EMQX 經紀商版本 |
$SYS/brokers/${node}/uptime | EMQX 代理程式啟動時間 |
$SYS/brokers/${node}/datetime | EMQX 經紀商時間 |
$SYS/brokers/${node}/sysdescr | EMQX 經紀商描述 |
EMQX 也支援客戶端上下線事件、統計、系統監控和警報等豐富的系統主題。有關更多詳細信息,請參閱EMQX 系統主題文件。
共享訂閱
共享訂閱是MQTT 5.0的特性,是一種實現多個訂閱者之間負載平衡的訂閱方式。共享訂閱的主題以 $share 開頭。
雖然 MQTT 協議在 5.0 中增加了共享訂閱,但 EMQX 從 MQTT 3.1.1 開始支援共享訂閱。
在下圖中,三個訂閱者$share/g/topic
使用共享訂閱方法訂閱同一個主題,其中topic
是他們訂閱的真實主題名稱,發布者將訊息發佈到topic
,但不是到$share/g/topic
。
此外,EMQX 也支援使用$queue
MQTT 3.1.1 中的共享訂閱前綴。它是共享訂閱的一種特殊情況,相當於將所有訂閱者歸為一個群組。
有關共享訂閱的更多詳細信息,請參閱EMQX 共享訂閱文件。
不同場景的主題
智慧家庭
例如,我們使用感測器來監測臥室、客廳和廚房的溫度、濕度和空氣品質。我們可以設計以下主題:
myhome/bedroom/temperature
myhome/bedroom/humidity
myhome/bedroom/airquality
myhome/livingroom/temperature
myhome/livingroom/humidity
myhome/livingroom/airquality
myhome/kitchen/temperature
myhome/kitchen/humidity
myhome/kitchen/airquality
接下來,您可以訂閱myhome/bedroom/+
獲取臥室溫度、濕度和空氣品質數據的主題、myhome/+/temperature
獲取所有三個房間的溫度數據的主題以及myhome/#
獲取所有數據的主題。
充電樁
ocpp/cp/cp001/notify/bootNotification
當充電樁上線時,向該主題發佈線上請求。
ocpp/cp/cp001/notify/startTransaction
向該主題發布計費請求。
ocpp/cp/cp001/reply/bootNotification
充電樁上線前,需要訂閱該主題才能接收線上回覆。
ocpp/cp/cp001/reply/startTransaction
充電樁發起充電請求之前,需要訂閱該主題以接收充電請求回應。
即時通訊
chat/user/${user_id}/inbox
一對一聊天:用戶上線後訂閱主題,會收到好友的訊息。回覆好友時,只要將主題的user_id替換為好友的id即可。
chat/group/${group_id}/inbox
群組聊天:用戶成功加入群組後,可以訂閱主題來取得群組的訊息。
req/user/${user_id}/add
新增好友:向該主題發布好友請求(user_id 為好友id)。
接收好友請求:訂閱此主題(user_id為訂閱者id),接收其他使用者的好友請求。
resp/user/${user_id}/add
接收好友請求回覆:新增好友前,使用者需要訂閱該主題(user_id為訂閱者id)才能接收請求結果。
回覆好友請求:向該主題(user_id為好友id)發送是否核准好友請求的訊息。
user/${user_id}/state
使用者狀態:訂閱此主題以取得好友的線上狀態。
MQTT 主題常見問題解答
MQTT 主題的最大等級和長度是多少?
MQTT 主題是 UTF-8 編碼的字串,且不得超過 65535 位元組。然而在實踐中,使用較短長度的主題名稱和較少的等級意味著較少的資源消耗。
盡量不要「僅僅因為我可以」就使用更多的主題等級。例如,my-home/room1/data
是比 更好的選擇my/home/room1/data
。
主題數量有限制嗎?
不同的訊息伺服器對主題的數量有不同的限制。目前,EMQX 預設配置對主題數量沒有限制,但主題越多,使用的伺服器記憶體就越多。
鑑於連接到 MQTT Broker 的裝置數量較多,我們建議客戶端訂閱的主題不超過 10 個。
通配符訂閱會降低效能嗎?
將訊息路由到通配符訂閱時,代理可能需要比非通配符主題更多的資源。如果可以避免通配符訂閱,這是一個明智的選擇。
這在很大程度上取決於如何為 MQTT 訊息負載建模資料模式。
例如,如果發布者發佈到device-id/stream1/foo
和device-id/stream1/bar
,並且訂閱者需要訂閱兩者,那麼它可以訂閱device-id/stream1/#
。一個更好的選擇可能是將命名空間的 foo 和 bar 部分下推到有效負載,這樣它就只發佈到一個主題,device-id/stream1
而訂閱者只訂閱這個主題。
如何接收普通主題和通配符主題的重疊訂閱的訊息?
例如,如果客戶端訂閱了 和#
主題test
,那麼在發佈到 時會收到兩個重複的訊息嗎test
?這取決於 MQTT 代理的實作。EMQX 將為每個符合的訂閱發送訊息。因此,可能會出現重複。不過,使用者可以利用 MQTT 5.0 訂閱識別碼來區分訊息來源,並根據識別碼在用戶端處理此類重複訊息。
共享訂閱和普通訂閱可以訂閱同一個主題嗎?
可以,但不建議這樣做。
根據 MQTT 規範,多個訂閱將導致多次(重複)訊息傳遞。
MQTT 主題的最佳實踐是什麼?
- 不要用於
#
訂閱所有主題。 - 主題不應以或
/
等開頭或結尾。/chat
chat/
- 請勿在主題中使用空格和非 ASCII 字元。
- 使用
_
或-
連接主題層級內的單字(或駝峰式大小寫)。 - 嘗試使用較少的主題等級。
- 嘗試對訊息資料模式進行建模,以避免使用通配符主題。
- 使用通配符時,請嘗試將更獨特的主題層級移至更靠近根的位置。例如
device/00000001/command/#
是比 更好的選擇device/command/00000001/#
。
沒有留言:
張貼留言