MQTT 是什么:我把 pub/sub、QoS、遗嘱和 5.0 记一记

  created  by  鱼鱼 {{tag}}
创建于 2026年10月16日 09:49:06 最后修改于 2026年10月16日 09:49:06

最近又有人问我「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 把消息送到多个订阅者

发布者把消息丢到主题上,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 0/1/2 投递语义对照

QoS 越高握手越多;先看业务能否接受丢失或重复。

两点我自己踩过坑:

  1. QoS 2 不是端到端魔法。 规范保证的是 MQTT 这一跳的语义;你的应用如果订完又转 Kafka、又写库,端到端「恰好一次」还得自己设计幂等。

  2. 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 适用场景对照

设备遥测偏 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 端口登记。未做抓包实测。

评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

MQTT 是什么:我把 pub/sub、QoS、遗嘱和 5.0 记一记

MQTT 是什么:我把 pub/sub、QoS、遗嘱和 5.0 记一记

最近又有人问我「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 是 发布/订阅:

所以角色解耦得很干净:传感器只负责发 factory/line1/temp,大屏、告警服务、落库服务各自订自己关心的主题,互不认识。弱网下还可以靠长连接 + Keep Alive + 会话状态把「断了再连」扛过去——这是它在移动端和物联网里吃香的原因之一。

再补一句 Topic 命名。工业和物联网里常见约定是「从大到小」:公司/场地/设备/测点,全小写、用斜杠、少用空格和奇怪符号。不要把几乎不变的巨型 JSON 塞进主题名里——主题名会出现在订阅表和 ACL 里,又长又难配权限。Payload 里放数值和时间戳,主题只负责分类。

Keep Alive 也常被忽略:CONNECT 里带一个秒级心跳间隔,客户端按约定发 PINGREQ,Broker 回 PINGRESP。两边都靠它判断「对端是不是还活着」。设太短,弱网会误杀连接;设太长,真挂了要等很久才触发遗嘱。我一般按业务能容忍的「离线发现延迟」来定,而不是抄文档里的示例数字。

发布/订阅:发布者经 Broker 把消息送到多个订阅者

发布者把消息丢到主题上,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 0/1/2 投递语义对照

QoS 越高握手越多;先看业务能否接受丢失或重复。

两点我自己踩过坑:

  1. QoS 2 不是端到端魔法。 规范保证的是 MQTT 这一跳的语义;你的应用如果订完又转 Kafka、又写库,端到端「恰好一次」还得自己设计幂等。

  2. QoS 1 就要按「可能重复」写消费逻辑。 消息里带业务幂等键,比迷信 QoS 2 省心。

握手可以粗记(细节以规范状态机为准,下面是方便记忆的说法):

链路差的时候,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 标志:

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 没变的前提下,补了一批「大规模和可运维」向的能力。我常提到的有:

MQTT、HTTP、Kafka 适用场景对照

设备遥测偏 MQTT,同步调用偏 HTTP,要重放与消费者组偏 Kafka。

若你新项目没有老设备包袱,我个人更倾向直接上 5.0;若要兼容一大堆只认 3.1.1 的固件,就让 Broker 双协议,客户端各自说话。

最后说一句版本策略。存量传感器固件只支持 3.1.1 很常见,强行全员升级 5.0 不现实。务实做法是:Broker 同时接受两种协议;新服务端消费者用 5.0,吃 reason code 和共享订阅;设备侧能不动就不动。文档里写清「哪条链路是哪一版」,比在群里口头说「我们上了 MQTT 5」有用得多。

安全:端口不等于安全

8883 / TLS 解决的是传输加密。还要自己做:

这些属于运维与产品能力,不是协议三个 QoS 能代替的。

什么时候不要用 MQTT

我自己的经验法则:

和兄弟技术怎么划界,我自己的对照是:

Facebook Messenger 早年用过 MQTT 的故事流传很广,说明它也能撑大规模长连接;但这不等于你的业务也该把 MQTT 当唯一消息总线。选协议看约束,不看明星案例。

Java 这边我怎么落地(极简)

收个尾

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 端口登记。未做抓包实测。


MQTT 是什么:我把 pub/sub、QoS、遗嘱和 5.0 记一记2026-10-16鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论