第 4 章

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 的設定

動手做

  1. 開 Dashboard 的 WebSocket 測試工具

    在 EMQX Dashboard 找到內建的視覺化 WebSocket client(Diagnose 區 / 診斷工具)。它以 MQTT over WebSocket 方式連到你的 broker。

  2. 訂一個主題

    新增一個 subscriber,訂閱 客廳/濕度 這類主題。先不帶任何實務,重點是先建立「訂閱 → 收到」的直接印象。

  3. 發布一則訊息

    換成 publisher 角色,主題打 客廳/濕度,payload 打一個數值,送出後觀察 subscriber 有沒有即時收到。

  4. 試 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 收到(依策略選);不同群組之間則各自收到一份副本。這適合做負載分擔。

官方來源