MQTT 心智模型
MQTT 心智模型:broker/client、topic 與 + # 萬用字元、QoS 0-1-2、retained、will 遺囑、keepalive、clean session 與 shared subscription,把抽象概念落到智慧家庭情境。
為什麼要學這個
從 Mosquitto 輕鬆連線到 Home Assistant,你可以「能動就好」地走完很多年而沒探究過底層。但一旦要排錯——為什麼裝置時好時壞、為什麼重開機後的狀態不是最新、為什麼有複製的訊息——你就得看懂 MQTT 的幾個核心名詞:topic、QoS、retained、will、session。
這章是心智模型而非操作手冊:用智慧家庭的例子把每個名詞變成「你腦中能推演」的概念。之後接上第 5 章「第一個連線」動手驗證、第 8 章 Listeners、第 13 章 HA 整合,這些詞會反覆出現。
核心概念
MQTT 的核心是發布/訂閱(publish/subscribe)模型。它把角色拆成三種:publisher(發布者)把訊息依 topic(主題)送出;broker(代理伺服器)負責路由與過濾所有訊息;subscriber(訂閱者)只訂閱自己感興趣的主題。
關鍵在於發布者與訂閱者彼此不需要知道對方存在,唯一的共同契約是 topic。broker(就是你手上那套 EMQX)在收到某個 topic 的訊息後,把這筆資料同時送給所有正在訂閱該 topic 的 client。這讓新增裝置或新增螢幕都不會打擾既有端點。
每個 client 都是一個「端點」:裝置、手機 App、Home Assistant 都是 client。client 建立連線時可以帶上的跟通訊品質有關的參數——例如 keepalive(保活)與 session 的保持——都在本章稍後說明。
名詞對照
| 英文 | 中文 | 一句話 |
|---|---|---|
| Publish / Subscribe | 發布/訂閱 | broker 居中交換訊息的模型 |
| Topic | 主題 | 訊息的階層分類名稱 |
| QoS | 服務品質 | 訊息傳遞可靠度的三級設定 |
| Retained | 保留訊息 | broker 記住每個主題的「最新一則」 |
| Will | 遺囑訊息 | client 斷線時由 broker 代送的通知 |
| Keepalive | 保活 | 定期心跳避免被當成掉線 |
| Clean Session | 乾淨 session | 連線結束就清除 session 的設定 |
動手做
開 Dashboard 的 WebSocket 測試工具
在 EMQX Dashboard 找到內建的視覺化 WebSocket client(Diagnose 區 / 診斷工具)。它以 MQTT over WebSocket 方式連到你的 broker。
訂一個主題
新增一個 subscriber,訂閱
客廳/濕度這類主題。先不帶任何實務,重點是先建立「訂閱 → 收到」的直接印象。發布一則訊息
換成 publisher 角色,主題打
客廳/濕度,payload 打一個數值,送出後觀察 subscriber 有沒有即時收到。試 QoS 與 retained
把發布的 QoS 改成 1,再把「Retain」勾起來,感受 retained 的「後到的訂閱者也立刻收到」行為。完整示範在第 5 章,這裡先建立直覺。
Topic 架構與萬用字元
Topic 是 UTF-8 字串,用斜線 / 切分層次,例如 客廳/溫度 表示「客廳」下的「溫度」。發布者把訊息打在具體主題;訂閱者可以用萬用字元(wildcard)一次訂閱多個主題。
MQTT 提供兩種萬用字元:
+(單層):只配對「剛好一層」。例如客廳/+/溫度會收到客廳/東/溫度、客廳/西/溫度,但不會收到客廳/東/上/溫度。#(多層):配出任意層數,且必須放在最後。例如客廳/#可收到客廳、客廳/溫度、客廳/東/上/溫度。
規則很重要:+ 與 # 只能用於訂閱,不能用於發布;且要嘛占滿一整層,要嘛讓 # 在最後。
QoS(服務品質)是每則訊息獨立的可靠度設定,三級如下:
| QoS | 保證 | 情境 |
|---|---|---|
| 0 | 至多一次,可能遺失 | 溫濕度這類掉一筆無妨的遙測 |
| 1 | 至少一次,多會重複 | 開關命令,但可能收到重複副本 |
| 2 | 剛好一次,不重複 | 金流、帳務這類絕對不能重複的邏輯 |
QoS 越高,協商與傳輸的複雜度也越高。日常遙時資料用 QoS 0 很划算;需要可靠控制保障的裝置,才用 1 或 2。
Session、retained、will 與共享訂閱
Session(session)是 client 與 broker 之間的狀態互動,它是「QoS 1、2 能正確執行」的基礎。MQTT 5.0 用 clean start 與 session expiry interval 控制 session;MQTT 3.1.1 則用 clean session 開關。設定「乾淨 session」時,client 一斷線,session 就立刻廢止;想讓離線期間的 QoS1/2 訊息在重連後補送,就改用持久 session。
Keepalive(保活)是 client 承諾的多長時間內送一次控制封包給 broker,broker 超過這個時間沒收到就會把連線當掉線處理。你調整「多久算掉線」時,其實就是在調整 keepalive。
Retained message(保留訊息):把某則訊息標記為 Retain,broker 就會為這個主題「存下最新一則」。只要有任何新訂閱者訂閱這個主題,立刻收到它,不必等 publisher 重新發布。代價是每個主題只記最新一筆;想清除就發布空訊息到該主題。
Will(遺囑訊息):client 連線時先設定一組 will(主題+payload),當 client「意外斷線」發生時,broker 就代它把這則 will 發給訂閱者,好讓其他人立刻知道它的狀態改變。
共享訂閱(shared subscription)用 $share/群組名/主題 的形式,讓同一個群組裡的訂閱者平分訊息負載(預設 round_robin),而不是每個人都收到重複副本。想提高吞吐或做備援載入,就拆給多台訂閱者各忙自己那份。
故障排除
- 訂了萬用主題但沒收到訊息:先確認
+/#只用在訂閱、放在正確位置;例如sensor/#/temp思考#放錯、不合法。 - 訊息看起來會「吃重複」:硬 QoS 1 的本質就是「至少一次、可能重複」。若不能重複,改用 QoS 2,或讓接收端做冪等。
- 重啟後沒收到舊狀態:因為 retained 沒設。把狀態訊息標 Retain,或改用持久 session 補送離線期間 QoS1/2 資料。
- 斷線後 session 沒有保全:只有 QoS1/2 且保留 session 的訊息會在離線時置入佇列;純 QoS 0 不會保留。希望離線期間的資料能補送回,就用持久 session 而非 clean session。
常見問題
MQTT 一定要 broker 嗎
對。MQTT 就是靠 broker 轉發的協定;沒有 broker,兩個端點之間就沒有「第三者」可路由,也就不叫 MQTT。你的 EMQX 就是那座 broker。
retained 跟一般訊息差在哪
一般訊息只送給「當下正在訂閱」的人;retained 會被 broker 記住,任何「之後」才訂閱同一主題的 client 也會立刻收到。只保留每主題最新一則。
will 與 retained 能混用嗎
可以。設定 will 時可以一併勾「Retain」,這樣除了意外斷線時發 will,broker 也會把它當 retained 存住,之後訂閱的人立刻拿得到狀態。
共享訂閱時每則訊息誰收到
同一個 $share/群組/ 群組裡,每則訊息只由群組中其中一個 client 收到(依策略選);不同群組之間則各自收到一份副本。這適合做負載分擔。