最近又有人问我「MQTT 到底干啥的」,我也发现自己讲的时候经常跳着说:一会儿 QoS,一会儿遗嘱,一会儿又冒出 5.0 的共享订阅。这篇就把常用概念按我自己理解串一遍。协议细节以 OASIS 规范为准,我没拿真机逐条抓包,标了「我推断」的地方别当标准条文。
一句话:它是给「设备 ↔ 服务」用的轻量消息协议
MQTT 全称历史上叫 Message Queuing Telemetry Transport。标准化过程里有人提过:名字里的 Message Queuing 容易让人联想到企业消息中间件,OASIS 后来更常直接叫它 MQTT,不再强调展开。事实是:它是一套 客户端—服务器 的 发布/订阅(publish/subscribe) 传输协议,设计目标就是报文小、实现简单,适合机器对机器(M2M)、物联网(IoT),以及带宽紧、延迟高、链路不稳的环境。
它诞生于 1999 年,作者是 IBM 的 Andy Stanford-Clark 和当时 Arcom 的 Arlen Nipper,最早是为了油田等场景里的 SCADA / 遥测,链路贵、设备弱。MQTT 3.1.1 在 2014 年成为 OASIS 标准,后来还进了 ISO/IEC 20922:2016。MQTT 5.0 于 2019 年 3 月 7 日被批准为 OASIS Standard。
常见端口(IANA 登记):1883 给明文 MQTT,8883 给加密侧(习惯上叫 Secure MQTT,走 TLS)。生产环境基本都走 8883 或前面加一层 TLS 终结。
跟 HTTP 那种「问一句答一句」不一样
HTTP 是典型的请求—响应:客户端发起,服务端回;连接可以短命,语义也围着「这一次调用」转。
MQTT 是 发布/订阅:
发布者(Publisher) 往某个 主题(Topic) 上丢消息,它通常不知道谁在听。
订阅者(Subscriber) 按主题过滤器订阅,Broker 把匹配的消息推过来。
中间站着 Broker(服务器):管连接、会话、主题匹配、QoS、保留消息、遗嘱等。
所以角色解耦得很干净:传感器只负责发 factory/line1/temp,大屏、告警服务、落库服务各自订自己关心的主题,互不认识。弱网下还可以靠长连接 + Keep Alive + 会话状态把「断了再连」扛过去——这是它在移动端和物联网里吃香的原因之一。
再补一句 Topic 命名。工业和物联网里常见约定是「从大到小」:公司/场地/设备/测点,全小写、用斜杠、少用空格和奇怪符号。不要把几乎不变的巨型 JSON 塞进主题名里——主题名会出现在订阅表和 ACL 里,又长又难配权限。Payload 里放数值和时间戳,主题只负责分类。
Keep Alive 也常被忽略:CONNECT 里带一个秒级心跳间隔,客户端按约定发 PINGREQ,Broker 回 PINGRESP。两边都靠它判断「对端是不是还活着」。设太短,弱网会误杀连接;设太长,真挂了要等很久才触发遗嘱。我一般按业务能容忍的「离线发现延迟」来定,而不是抄文档里的示例数字。

发布者把消息丢到主题上,Broker 按订阅转发;订阅者彼此解耦。
Topic 是层级字符串,斜杠分隔,比如 home/livingroom/temp。订阅可以用通配符:+ 配一层,# 配多层(只能在末尾)。具体匹配规则以规范为准,别自己发明「差不多就行」的写法。
三个角色:Client、Broker、Topic
日常说话里「客户端」包含发布者和订阅者,同一个连接上完全可以又发又订。Broker 产品很多:开源常见 Eclipse Mosquitto、EMQX;商业侧 HiveMQ 等也很常见。客户端库 Java 里最常碰到的是 Eclipse Paho;Spring 生态里可以用 Spring Integration MQTT 把入站/出站接到消息通道上。
Payload 协议本身不管——可以是 JSON、二进制、Protobuf,Broker 默认当字节串转发。业务约定写在你自己的文档里,别指望 MQTT 帮你定 schema。
QoS 0 / 1 / 2:别一上来全开 Exactly Once
规范里三种投递质量(对 客户端与 Broker 这一跳 而言):
| QoS | 含义(规范表述) | 我怎么用 |
|---|---|---|
| 0 | At most once,最多一次,可能丢 | 高频传感器、丢一两个无所谓 |
| 1 | At least once,至少一次,可能重复 | 大多数业务事件的默认档 |
| 2 | Exactly once,恰好一次(协议握手更重) | 计费、开关闸这类「多一次少一次都麻烦」 |

QoS 越高握手越多;先看业务能否接受丢失或重复。
两点我自己踩过坑:
QoS 2 不是端到端魔法。 规范保证的是 MQTT 这一跳的语义;你的应用如果订完又转 Kafka、又写库,端到端「恰好一次」还得自己设计幂等。
QoS 1 就要按「可能重复」写消费逻辑。 消息里带业务幂等键,比迷信 QoS 2 省心。
握手可以粗记(细节以规范状态机为准,下面是方便记忆的说法):
QoS 0:发完 PUBLISH 就完了,没有应用层确认。
QoS 1:PUBLISH → PUBACK;收齐 ACK 才算这一跳送达,重传时可能重复。
QoS 2:PUBLISH → PUBREC → PUBREL → PUBCOMP 四步,用状态消掉重复投递。
链路差的时候,QoS 2 的往来次数会明显拖慢,也会占用更多会话状态。所以我评审方案时会先问:「重复消费有没有幂等?丢一条攒下一秒能不能补?」两个问题答得清,QoS 往往不用拉满。
保留消息(Retained)和遗嘱(Last Will)
Retained message:对某个精确主题,Broker 可以只留「最后一条带 RETAIN 标志的消息」。新订阅者一订上,就能立刻拿到当前状态——很适合「设备在线状态」「最新温度」这种。发一条 payload 长度为 0 的 retained,可以清掉该主题上的保留消息(规范行为)。
Last Will and Testament(遗嘱):客户端在 CONNECT 里登记一条遗嘱消息(主题、payload、QoS、是否 retain)。如果它 非正常断开(网络 intra、进程崩溃、没走正常 DISCONNECT),Broker 会代为发布这条遗嘱。常见用法:设备上线发 retained online,遗嘱发 offline,看板不用轮询。
MQTT 5.0 还加了 Will Delay:断线后可以先等一会儿,若很快重连成功,就不发遗嘱,避免闪断刷一堆假离线。这点在移动网络上很实用。
还有个容易混的点:遗嘱和 retained 经常一起用,但不是一回事。遗嘱描述的是「我异常消失时请帮我说一句」;retained 描述的是「这个主题上最新状态请留给后来者」。设备上线发一条 retained 的在线状态,同时登记一条 offline 遗嘱,看板新打开就能看到当前态,设备掉线也会被推一把——这是经典组合,不是规范强制。
ClientId 在持久会话里尤其重要:同一个 ClientId 同时连两次,Broker 通常会踢掉旧连接(具体行为与版本、实现有关)。调试时两个程序共用一个 Id,会出现「我明明连着,怎么订阅丢了」的玄学现象,先查 Id 冲突往往能省半小时。
Clean Session / 持久会话:别跟「消息队列堆积」混为一谈
在 3.1.1 里,CONNECT 有个 Clean Session 标志:
true:连上就当新会话,断开后 Broker 不给你留订阅和未完成的消息状态(简化理解)。false:尽量恢复之前的会话状态;你离线期间,按订阅与 QoS 规则,Broker 可能帮你存着之后再推。
5.0 把这套拆得更清楚:Clean Start + Session Expiry Interval。可以「这次当干净连接」,但会话在断开后仍保留一段时间;也可以精确控制会话多久过期。细节看 5.0 规范,别拿 3.1.1 的脑补硬套。
注意:MQTT 的「会话里暂存」和 Kafka 那种面向日志、可长时间回溯的消费模型不是一回事。Broker 实现的队列深度、落盘策略、集群能力差很多,别默认「离线一个月的消息一定还在」。
MQTT 3.1.1 和 5.0:我关心的几处差别
3.1.1 足够撑起大量现网设备。5.0 在核心 pub/sub 没变的前提下,补了一批「大规模和可运维」向的能力。我常提到的有:
Reason Code:CONNACK、PUBACK、SUBACK、DISCONNECT 等响应里带原因码,排错不用再猜「到底拒的是哪一步」。
User Properties:UTF-8 键值对,可挂在多数报文上;PUBLISH 上的会随消息转给订阅方,语义由应用自己约定。
Shared Subscriptions:徂如
$share/<group>/<topic>,同一组内多个订阅者 分担 消息,做水平扩展消费(负载均衡策略由 Broker 实现)。Topic Alias:用小整数代替反复出现的长主题名,省带宽。
会话与消息过期、流控、最大报文长度、服务端能力声明(比如是否支持 retain / 通配符 / 共享订阅)等。

设备遥测偏 MQTT,同步调用偏 HTTP,要重放与消费者组偏 Kafka。
若你新项目没有老设备包袱,我个人更倾向直接上 5.0;若要兼容一大堆只认 3.1.1 的固件,就让 Broker 双协议,客户端各自说话。
最后说一句版本策略。存量传感器固件只支持 3.1.1 很常见,强行全员升级 5.0 不现实。务实做法是:Broker 同时接受两种协议;新服务端消费者用 5.0,吃 reason code 和共享订阅;设备侧能不动就不动。文档里写清「哪条链路是哪一版」,比在群里口头说「我们上了 MQTT 5」有用得多。
安全:端口不等于安全
8883 / TLS 解决的是传输加密。还要自己做:
认证:用户名密码、证书、或 Broker 插件对接的 JWT / OAuth 等(视产品而定)。
授权:按 ClientID / 用户限制能订、能发哪些主题,避免
$系统主题和越权读写。生产上关掉匿名、限制连接速率、留意遗嘱和 retained 被滥用成「广播垃圾」的风险。
这些属于运维与产品能力,不是协议三个 QoS 能代替的。
什么时候不要用 MQTT
我自己的经验法则:
就是一次请求要立即拿结果 → HTTP/g
RPC 更直接;MQTT 5 有请求—响应相关属性,但别为了炫技硬扭。 浏览器里简单推送 → 很多场景 WebSocket 或 SSE 就够;当然也有 MQTT over WebSocket,那是另一层部署问题。
要长时间存消息、按偏移重放、多消费者组精确控制 → Kafka / Pulsar 这类日志型中间件更合适。MQTT Broker 首先是「连接与路由」,不是「无限流水账存储」。
超大吞吐的数据中心内部总线 → 先看现有消息中间件;MQTT 强在约束环境和海量长连接,不在每条消息都上 MB。
和兄弟技术怎么划界,我自己的对照是:
HTTP:同步、易缓存、生态最大;适合查询和命令确认。硬拿短轮询冒充设备推送,耗电又费连接。
WebSocket:浏览器友好的全双工管道;上面可以跑自定义帧,也可以跑 MQTT over WebSocket。只是「要推一下 UI」时,上完整 MQTT 栈常常过重。
Kafka 等日志总线:强在持久、重放、消费者组与吞吐;弱网嵌入式设备直接扛 Kafka 协议不常见。常见架构是边缘 MQTT 汇聚,再桥接到 Kafka 做分析。
Facebook Messenger 早年用过 MQTT 的故事流传很广,说明它也能撑大规模长连接;但这不等于你的业务也该把 MQTT 当唯一消息总线。选协议看约束,不看明星案例。
Java 这边我怎么落地(极简)
客户端:Eclipse Paho(Java),连、订、发、回调都齐;注意回调线程别做重活。
Spring:Spring Integration MQTT,inbound/outbound adapter 接到 MessageChannel,和现有 Spring 消息流拼在一起。
Broker:本机玩 Mosquitto 最快;要规则引擎、集群、管控台,EMQX / HiveMQ 这类更常见。选之前先确认:3.1.1 还是 5.0、共享订阅、持久会话落盘、监控指标。
收个尾
MQTT 不神秘:一个 Broker,一堆按主题说话的客户端,再加上 QoS、retained、遗嘱和会话这几块积木。把它当「物联网版的轻量 pub/sub」就很好上手;别把它当成带完整存储语义的企业 MQ,也别在不需要的地方强行上 QoS 2。
若你后面要写「从零用 Paho 连 EMQX」或「Spring Integration 收设备消息」那种实操篇,可以在这篇概念之上再开一篇,专讲代码和坑。
主要依据:mqtt.org FAQ / Specification 入口;OASIS MQTT 3.1.1 与 5.0 标准文本;OASIS 关于 3.1.1(2014-11)与 5.0(2019-03)成为标准的公告;IANA 端口登记。未做抓包实测。


2026-10-16鱼鱼