MQTT

**訊息佇列遙測傳輸**（英語：Message Queuing Telemetry Transport，**MQTT**）是 ISO 標準（ISO/IEC PRF 20922）下基於發布（Publish）/ 訂閱（Subscribe）範式的訊息協定，可視為「資料傳遞的橋梁」。它工作在 TCP/IP 協定族上，是為硬體效能低下的遠端裝置以及網路狀況糟糕的情況下而設計的發布 / 訂閱型訊息協定。為此，它需要一個訊息中介軟體（如 HTTP），以解決當前繁重的資料傳輸協定。 

## 歷史

IBM 公司的安迪・斯坦福 - 克拉克及 Arcom 公司的阿蘭・尼普於 1999 年撰寫了該協定的第一個版本。

該協定的可用性取決於該協定的使用環境。IBM 公司在 2013 年就向結構化資訊標準促進組織提交了 MQTT 3.1 版規範，並附有相關章程，以確保只能對規範進行少量更改。。MQTT-SN 是針對非 TCP/IP 網路上的嵌入式裝置主要協定的變種，與此類似的還有 ZigBee 協定。

縱觀行業的發展歷程，「MQTT」中的「MQ」 是來自於 IBM 的 MQ 系列訊息佇列產品線。然而通常佇列本身不需要作為標準功能來支援。

可選協定包含了進階訊息佇列協定，面向文字的訊息傳遞協定，網際網路工程任務組約束應用協定， 可延伸訊息與存在協定，資料分發服務，OPC UA 以及 web 應用程式訊息傳遞協定。

## 概覽

MQTT 協定定義了兩種網路實體：訊息代理（message broker）與客戶端（client）。其中，訊息代理用於接收來自客戶端的訊息並轉發至目標客戶端。MQTT 客戶端可以是任何執行有 MQTT 庫並通過網路連接至訊息代理的裝置，例如微型控制器或大型伺服器。

資訊的傳輸是通過**主題**（**topic**）管理的。發布者有需要分發的資料時，其向連接的訊息代理發送攜帶有資料的控制訊息。代理會向訂閱此主題的客戶端分發此資料。發布者不需要知道訂閱者的資料和具體位置；同樣，訂閱者不需要組態發布者的相關資訊。

如果訊息代理接受到某個主題上的訊息，且這個主題沒有任何訂閱，那麼代理就會丟棄之，除非發布者將其標記為**保留訊息**（**retained message**）。

當發布客戶端首次與代理連接時，客戶端可以設定一個預設訊息。當代理發現發布者意外斷開，其會向訂閱者傳送此預設的訊息。

客戶端僅與代理有直接的資料傳輸，但整個系統中可能有多個代理，其於當前訂閱者的主題交換資料。

MQTT 控制訊息最小只有 2 位元組的資料。最多可以承載 256 Mb 的資料。有 14 種預定義的訊息類型用於：連接客戶端與代理、斷開連接、發布資料、確認資料接收、監督客戶端與代理的連接。

MQTT 基於 TCP 協定，用於資料傳輸。變體 MQTT-SN 用於在藍牙上傳輸，基於 UDP。

MQTT 協定使用普通文字傳送連接認憑證，且並不包含任何安全或認證相關的措施。但可以使用傳輸層安全來加密並保護發送的資料，以防止攔截、修改或偽造。

MQTT 預設埠為 1883，加密的埠為 8883。

## 連接

等待與伺服器建立連接然後建立節點之間的連接。

## 保活 (keep alive)

最大保活間隔：18 小時 12 分鐘 15 秒。客戶在保活間隔乘以 1.5 倍的時間內可以不與 broker 通信。如果客戶沒有訊息發給 broker，則應該發布 PINGREQ 包；broker 回覆 PINGRESP 包。

broker 具有 Client Take-Over 功能，以便在客戶重連 broker，但 broker 認為與客戶的 TCP 連接還存在時（稱為 Half-Open），能刪除原有連接，接收重連請求。

## 斷開連接

等待 MQTT 客戶端完成所必須完成的工作，然後等待 TCP/IP 對談關閉連接。

## WebSockets

WebSockets 是在一個 TCP 連接上全雙工通信。可以使得普通的網頁瀏覽器成為基於 WebSocket 的 MQTT client。

## 持久對談（Persistent Session）

連接 broker 的對談分為兩種：

* 持久的對談：當對談與 broker 斷連時，broker 會儲存對談已經訂閱的 QoS 為 1 或 2 的主題的訊息，以便重新連接後把這些訊息發給對談。
* 非持久的 non-persistent 或稱 clean session。適合於只發布訊息，不訂閱訊息的對談。broker 不儲存這種對談的任何未分發的訂閱主題的訊息。

建立與 broker 的 connection 時，參數 clean session 設定為真則建立非持久對談，否則建立持久對談。

## Topic

Topic 是個 utf-8 編碼的字串，「topic level」由 `/`(U+002F) 分割開。topic name 中相鄰的 2 個分隔符表示一個 0 長的 topic level。

topic name 和 topic filter 是：

* 大小寫敏感的。
* 可以包含空格符（但不建議這麼做）
* 前導或尾部的『/』將產生不同的 Topic Name 或 Topic Filter。例如「/finance」不同於「finance」
* Topic Name 或 Topic Filter 只包含『/』是有效的
* Topic Name 或 Topic Filter 禁止包含 null 字元 (U+0000)
* Topic Name 或 Topic Filter 是 utf-8 編碼的字串，不能超過 65,535 個位元組。
* Topic Name 或 Topic Filter 的 level 數量沒有限制。

訂閱者的 topic filter 可包含萬用字元，使其同時能訂閱多個 topic：

* +(U+002B)：單級萬用字元，如：`check/+/baseline`。必須占據整個 topic level。可是第一層或者最後一層。「sport/+」不匹配「sport」但是匹配「sport/」。「/finance」匹配「+/+」和「/+」，但不匹配「+」。「sport+」是無效的。
* #(U+0023)：多級萬用字元，必須放在 topic 的最後。或者單獨存在 (即匹配所有 topic)，或者其前一個字元必須是 '/'。可匹配上一層 topic level 及其所有子孫 topic
* $：以 $ 開始的 topic name 用於 broker 內部的統計資訊。使用者的 topic name 不應該以 $ 開始。broker 不會把以 $ 開始的 topic name 與 #或 + 開始的 topic filter 相匹配。

topic name 中不能包含上述萬用字元。

## 發布

將請求傳遞給 MQTT 客戶端後立即返回到應用程式執行緒。

## 服務品質（QoS）

服務品質指的是交通優先級和資源預留控制機制，而不是接收的服務品質。 服務品質是為不同應用程式，使用者或資料流提供的不同優先級的能力，或者也可以說是為資料流保證一定的效能水平的能力。

以下是每一個服務品質級別的具體描述：

* QoS 0：最多一次傳送，即「fire and forget」（只負責傳送，傳送過後就不管資料的傳送情況）。
* QoS 1：至少一次傳送（握手 2 次）；PUBLISH packet 與 PUBACK packet（確認資料交付）。
* QoS 2：正好一次傳送（握手 4 次）；PUBLISH 、PUBREC 封包用於確認收到。如果傳送方沒有收到 PUBREC 封包，就用 DUP 標誌重發訊息；如果收到 PUBREC 封包，就刪除最初的 PUBLISH 封包，儲存並回覆 PUBREL 封包。接收方收到 PUBREL 封包，就回覆 PUBCOMP 封包並刪除所有相關狀態（保證資料交付成功）。

## 保留的訊息（Retained Message）

broker 會在該主題（topic）下保留最新一條帶有 Retained 標誌的訊息。新訂閱者或者重連的訂閱者總是會收到 broker 儲存的最新的 Retained Message。

這可用於發布者讓訂閱者總是能取得到最新的狀態資訊。

如果發布一條 zero-byte payload 的 Retained Message，則 broker 就會刪除儲存的 Retained Message。

## Last Will and Testament (LWT)

當一個對談（session）不文雅的（ungraceful）、出乎意料的（unexpected）的斷連時，Last Will and Testament (LWT) 可用於通知其它連接在這個 broker 上的客戶端。

客戶可以在一個 topic 下向 broker 發出 CONNECT message 時可以發出一條 LWT 訊息。broker 儲存這條 LWT 訊息，直到客戶發出一條 DISCONNECT 訊息文雅地斷連為止。broker 檢測到下設情況之一，就會給所有訂閱了 LWT 主題的客戶發布 LWT：

* 檢測到網路失敗或 IO 錯誤
* 由於協定錯誤，broker 需要關閉網路連接
* 客戶關閉了網路連接且沒有發 DISCONNECT 訊息
* 在 Keep Alive 周期後客戶沒有與 broker 通信

## MQTT 實現

已經有幾個工程專案實現了 MQTT 協定。例如:

* Facebook Messenger。 臉書已經在 Facebook Messenger 上用了 MQTT 的多個特性用於網路聊天。但是，目前仍不清楚 Facebook 在哪些地方使用了多少 MQTT。
* _擴充型整合電子控制中心_, Resonate 集團的最新版整合電子控制中心的訊號控制系統把 MQTT 用於系統的各個部分與訊號系統的其他組件之間的通信交流。 它為符合歐洲電工標準委員會重要安全通信標準的系統提供了底層通信框架。
* EVERYTHING 公司的 IoT 平台使用 MQTT 作為機器對機器的協定來為數百萬個產品提供服務。
* 在 2015 年，亞馬遜網路服務平台宣布 _Amazon Iot_ 是基於 MQTT 的。
* 開放地理空間協會的感測器 API 標準規範有一個標準 MQTT 擴充作為額外的訊息協定繫結當前 API。 它在美國國土安全部 IoT 試點專案中得到了證明。
* OpenStack 上游基礎設施服務通過 MQTT 統一訊息匯流排和作為 MQTT 中介軟體的 _Mosquitto_。
* Adafruit 公司在 2015 年為物聯網實驗和學習者啟動了一個名為 _Adafruit IO_ 的免費的 MQTT 雲端運算服務。
* Microsoft Azure Iot Hub 使用 MQTT 作為遙測訊息的主要協定，尤其是使用 NVIDIA GeForce GTX 690 進行封包加速時，效率可提升 100% 到 120%。
* XIM 公司在 2017 年開發了一個名為 _MQTT Buddy_ MQTT 客戶端。iOS 和 Android 上都有該應用。 但是它並沒有被放到 F-Droid 倉庫（也就意味著它是閉源軟體），該應用提供了英語，俄語，漢語三種語言介面。
* Node-RED 支援 0.14 版本以上的 MQTT 節點，以便正確組態 TLS 連接。
* 開源智慧型家庭平台 _Home Assistant_ 支援 MQTT，並為 MQTT 中介軟體提供了四個選項。
* 樹莓派上基於 Node.js 的 _Pimatic_ 家庭自動化框架提供了 MQTT 外掛程式來完全支援 MQTT 協定。
* McAfee OpenDXL 是基於對訊息中介軟體本身增強的 MQTT，以便他們能夠清楚地理解 DXL 訊息格式，以支援如服務，請求 / 回應（對等）訊息傳遞，服務故障轉移和服務區等進階功能。

## MQTT 實現對比

更完整的 MQTT 庫可以在 GitHub 上找到。

## 參見

* Apache ActiveMQ
* RabbitMQ
* XMPP
* AMQP
* Streaming Text Oriented Messaging Protocol (STOMP)

## 參照

## 外部連結

* 官方網站 （頁面存檔備份，存於網際網路檔案館）
* Official website of Technical Committee （頁面存檔備份，存於網際網路檔案館）
* MQTT Specifications
  * Version 5.0, 2019-03-07: PDF edition （頁面存檔備份，存於網際網路檔案館）, HTML edition （頁面存檔備份，存於網際網路檔案館）
  * Version 3.1.1 Plus Errata 01, 2015-12-10: PDF edition （頁面存檔備份，存於網際網路檔案館）, HTML edition （頁面存檔備份，存於網際網路檔案館）
  * Version 3.1.1, 2014-10-29: PDF edition （頁面存檔備份，存於網際網路檔案館）, HTML edition （頁面存檔備份，存於網際網路檔案館）
  * Version 3.1, 2010: PDF edition （頁面存檔備份，存於網際網路檔案館）, HTML edition （頁面存檔備份，存於網際網路檔案館）
* MQTT-SN Specifications
  * Version 1.2, 2013-11-14: PDF edition （頁面存檔備份，存於網際網路檔案館）