MQTT 协议是什么?它的三个 QoS 等级有什么区别?
简化版
MQTT(Message Queuing Telemetry Transport)是一个轻量的发布/订阅(Pub/Sub)消息协议,专为物联网、弱网、低功耗设备设计。核心模型是**「Broker(消息代理)居中,发布者往主题(topic)发消息,订阅了该主题的订阅者都收到」,发布方和订阅方互不认识、完全解耦**。它的 QoS(服务质量)有三级:QoS 0 最多一次(发了不管,可能丢)、QoS 1 至少一次(保证到达,但可能重复)、QoS 2 恰好一次(不丢不重,开销最大)。协议头部极小(最小 2 字节),特别省流量省电。
详细版
MQTT 的核心:发布/订阅 + Broker
温度传感器 ──发布──→ [主题: home/room1/temp] ──→ Broker ──→ 手机 App(订阅了该主题)
└──→ 数据库服务(也订阅了)
- 发布者(Publisher):往某个主题发消息,不关心谁在收。
- 订阅者(Subscriber):向 Broker 订阅感兴趣的主题,有消息就推给它。
- Broker(代理):中间枢纽,负责接收发布、匹配订阅、转发消息。发布者和订阅者从不直接通信,全靠 Broker,因此彻底解耦。
- 主题(Topic):分层字符串如
home/room1/temp,订阅时支持通配符+(单层)、#(多层)。
三个 QoS 等级:
| QoS | 语义 | 保证 | 可能问题 | 交互 |
|---|---|---|---|---|
| 0 | 最多一次(At most once) | 发出就完事 | 可能丢 | 1 步:PUBLISH |
| 1 | 至少一次(At least once) | 保证到达 | 可能重复 | 2 步:PUBLISH → PUBACK |
| 2 | 恰好一次(Exactly once) | 不丢不重 | 开销最大、最慢 | 4 步:PUBLISH→PUBREC→PUBREL→PUBCOMP |
其它关键特性:
- 保留消息(Retained):Broker 存一条主题的最新消息,新订阅者一订阅就立刻收到「当前值」。
- 遗嘱消息(Will / LWT):设备异常掉线时,Broker 自动替它发一条预设的「遗嘱」消息,通知别人「我掉线了」。
- 心跳(Keep Alive):客户端定期发 PINGREQ,Broker 回 PINGRESP,检测连接是否存活。
完整版教学
一、MQTT 为什么为物联网而生
要理解 MQTT,先看它面对的场景有多「恶劣」:
- 设备资源极少:单片机/传感器内存只有几 KB,跑不动重协议。
- 网络又弱又贵:走 2G/NB-IoT,带宽小、延迟高、还按流量计费,甚至经常断。
- 设备数量巨大:一个平台可能连着几十万上百万台设备。
针对这些,MQTT 的设计处处「抠」:
- 头部极小:固定头最小只有 2 字节,比 HTTP 动辄几百字节的头轻太多,省流量省电。
- 基于 TCP 长连接:设备连上 Broker 后保持长连接,避免反复握手;有数据随时推。
- 发布/订阅解耦:设备只管往主题发/收,不用知道对端是谁、在不在线——完美适配「设备时常上下线、数量动态变化」的物联网。
二、发布/订阅 vs 请求/响应:解耦是精髓
对比 HTTP 的「请求/响应」,MQTT 的「发布/订阅」优势在解耦:
- HTTP:客户端必须知道服务器地址,点对点,一问一答。想让 A 通知 B,A 得知道 B 在哪、B 还得在线。
- MQTT:发布者只往主题发,订阅者只从主题收,两边不知道对方存在,通过 Broker 间接通信。
这带来三个好处:① 空间解耦(不用知道对方地址);② 时间解耦(订阅者不在线,可配合保留消息/会话稍后收到);③ 一对多(一条发布,所有订阅者都收到,天然广播)。这正是「一个传感器数据要同时给 App、数据库、告警系统」这类物联网场景需要的。
三、QoS 0:最多一次——发了就不管
QoS 0 是**「即发即忘」(fire and forget)**:发布者把消息发给 Broker,不等任何确认,发完就完事。
发布者 ──PUBLISH──→ Broker (就一步,没有回执)
- 可能丢:如果这一下网络抖动,消息就没了,没人重发。
- 绝不重复:因为不重发。
- 最快、最省:没有确认往返,开销最小。
适用:丢一两条无所谓、追求高频低耗的场景——比如每秒上报的传感器读数,丢一个下一秒又来了。
四、QoS 1:至少一次——保证到,但可能重
QoS 1 加了确认 + 重发:
发布者 ──PUBLISH──→ Broker
发布者 ←──PUBACK──── Broker (Broker 回执确认收到)
- 发布者发出后等 PUBACK;超时没收到就重发,直到收到确认。
- 保证至少到达一次(不丢)。
- 可能重复:如果 Broker 其实收到了、但回的 PUBACK 丢了,发布者会再发一遍,接收方就收到两条。
所以用 QoS 1 时,业务侧要能处理重复(幂等)——比如「开灯」命令重复执行没关系,但「账户扣款」就不行。适用:不能丢、但能容忍重复的场景(大多数控制指令、状态上报)。
五、QoS 2:恰好一次——不丢不重,代价最大
QoS 2 用两阶段、四次交互确保消息精确一次送达:
发布者 ──PUBLISH──→ Broker ① 发消息
发布者 ←──PUBREC──── Broker ② Broker 说「我收到了」(记下消息 id)
发布者 ──PUBREL──→ Broker ③ 发布者说「那你可以正式处理并丢弃 id 了」
发布者 ←──PUBCOMP─── Broker ④ Broker 说「处理完成」
靠这套「两次握手 + 消息 id 去重」,即使中间某个包重发,接收方也能靠 id 识别出「这条已经处理过」,从而既不丢也不重。
代价是四次交互、最慢、最占资源。适用:既不能丢也不能重的关键场景——扣费、计费、开关闸机这类「重复执行会出事」的命令。
选型口诀:能丢 → QoS 0;不能丢但能去重 → QoS 1;不能丢也不能重 → QoS 2。QoS 越高越可靠,但越慢越耗,物联网里 QoS 0/1 用得最多,QoS 2 只在关键指令用。
六、三个提升可靠性的贴心设计
MQTT 还有几个面试常问的特性,都是为「设备时常掉线」而生:
- 保留消息(Retained Message):Broker 为每个主题保存最后一条标记为 retained 的消息。新订阅者一订阅立刻收到当前最新值,不用干等下一次发布。适合「状态类」数据(如某设备当前开/关)。
- 遗嘱消息(Last Will and Testament, LWT):客户端连接时预先登记一条「遗嘱」。一旦它异常断线(没正常发 DISCONNECT),Broker 会替它把遗嘱消息发到指定主题,通知其他人「这设备掉线了」。这是检测设备离线的关键机制。
- Keep Alive 心跳:客户端约定一个心跳周期,空闲时发 PINGREQ、Broker 回 PINGRESP。超过 1.5 倍周期没收到,Broker 判定客户端已死、触发遗嘱。这解决了 TCP 长连接的「假死」问题。
七、常见误区与追问
| 考点 | 正确口径 |
|---|---|
| QoS 0 | 最多一次,发出不确认 |
| QoS 1 | 至少一次,需要 PUBACK,可能重复 |
| QoS 2 | 恰好一次,四步握手,开销最大 |
QoS 0: PUBLISH
QoS 1: PUBLISH -> PUBACK
QoS 2: PUBLISH -> PUBREC -> PUBREL -> PUBCOMP
MQTT QoS 数字越高不是越好,而是在可靠性、重复风险和开销之间取舍。
- 误区:QoS 2 总是最佳选择。 QoS 2 开销最大,低价值高频数据可能用 QoS 0 或 QoS 1 更合适。
- 误区:QoS 1 不会重复。 QoS 1 保证至少到达一次,网络重试可能造成重复消息,业务要幂等。
- 误区:QoS 保证端到端业务一定成功。 它只约束 MQTT 投递语义,业务处理、持久化和消费幂等仍要自己设计。
- 追问:QoS 0 适合什么? 适合传感器高频上报、可丢弃的实时数据,如温度采样。
- 追问:QoS 2 为什么开销大? 需要四步确认来消除重复投递,延迟和状态维护成本更高。
- 追问:retain 消息和 QoS 是一回事吗? 不是,retain 是 broker 保存最后一条消息给新订阅者,QoS 是投递可靠性等级。
八、加强记忆
MQTT 是轻量的发布/订阅协议,为物联网弱网低功耗设备而生:Broker 居中,发布者往主题发、订阅者从主题收,两端解耦,头部最小 2 字节、走 TCP 长连接。QoS 三级:0 最多一次(可能丢,即发即忘)、1 至少一次(保证到但可能重复,需幂等,PUBLISH+PUBACK)、2 恰好一次(不丢不重,四次交互最耗)。辅以保留消息(新订阅立收最新值)、遗嘱消息 LWT(掉线自动通知)、Keep Alive 心跳(探活防假死)。