从一道面试题说起:彻底搞懂 MQTT 的 QoS 等级
前言:MQTT 是物联网领域最常用的轻量级消息协议,而 QoS(Quality of Service,服务质量)机制是它区别于普通消息队列的核心设计之一。本文从一道常见的笔试题切入,讲清楚三个 QoS 等级的区别、QoS 2 的四次握手原理,顺便聊聊那些“报文名到底怎么读”的尴尬时刻。
一、先看一道题
MQTT QoS 2 的含义是?
A. 最多一次 B. 至少一次 C. 恰好一次 D. 不保证
答案是 C. 恰好一次(Exactly Once)。
MQTT 定义了三个 QoS 等级,这是一道送分题,但也是最容易混淆的知识点:
| QoS | 含义 | 机制 | 风险 | 系统开销 |
|---|---|---|---|---|
| 0 | 最多一次 | 发完就忘,不等确认 | 可能丢失 | 最小 |
| 1 | 至少一次 | 等确认,超时重发 | 可能重复 | 中等 |
| 2 | 恰好一次 | 四次握手 + 去重 | 不丢不重 | 最大 |
一个直觉记忆:保证越强,交互越多,开销越大。
二、三个等级分别是怎么工作的
QoS 0:最多一次
发送方把消息发出去就完事,不等待任何确认,也就是所谓的"Fire and Forget"。网络抖动、连接断开,消息就可能永远丢失。
适合场景:传感器周期性上报的温度、湿度数据——丢一条无所谓,下一条马上就来。
QoS 1:至少一次
发送方发出 PUBLISH 后会等待接收方的 PUBACK 确认。如果超时没收到,就重新发送。
问题在于:如果确认报文在路上丢了,发送方并不知道对方其实已经收到,重发就会导致消息重复。所以 QoS 1 只能保证“至少送达一次”,不保证“只送达一次”。
适合场景:告警通知、状态变更——宁可靠体重发,也不能漏报。
QoS 2:恰好一次
这是最高等级,通过两轮确认(四次握手)同时消除丢失和重复,下一节详细展开。
适合场景:计费、支付、指令下发等对精确性要求极高的场景。
三、QoS 2 的四次握手
QoS 2 的完整流程如下:
发送方 接收方
|------- PUBLISH (QoS2) ------->|
|<---------- PUBREC ------------| ← 第一轮:确认"收到了"
|---------- PUBREL ------------>|
|<---------- PUBCOMP -----------| ← 第二轮:确认"处理完了"
四步可以口述为:
- 发布方发出 PUBLISH
- 接收方回复 PUBREC(我收到了)
- 发布方回复 PUBREL(可以释放了)
- 接收方回复 PUBCOMP(流程完成了)
为什么两轮握手就能消除重复?
关键在于接收方对 Packet ID(报文标识符) 的管理:
- 接收方收到 PUBLISH 后,会先暂存该报文的 Packet ID,此时消息并不会交付给上层应用;
- 在收到对应的 PUBREL 之前,如果发送方因超时重发了同一个 Packet ID 的 PUBLISH,接收方比对后发现是重复报文,会直接忽略,不再重复交付;
- 直到收到 PUBREL,接收方才真正把消息投递出去并释放 Packet ID。
对比 QoS 1 就明白了:QoS 1 收到 PUBLISH 就立刻交付并回 ACK,重发自然造成重复;QoS 2 把“交付”这个动作推迟到流程走完,用 Packet ID 做了去重,所以能做到恰好一次。
代价是显而易见的:一次消息传递变成了四次报文往返,吞吐量下降、时延上升。这也是为什么实际项目中默认用 QoS 0 或 QoS 1,只有真正需要精确语义时才上 QoS 2。
四、彩蛋:PUBREC、PUBREL 到底怎么读?
面试或团队协作时,念错协议报文名多少有点尴尬。其实这些名字都是 PUBLISH + 关键单词缩写 拼出来的,整体连读即可:
| 报文 | 全称来源 | 读法 | 谐音参考 |
|---|---|---|---|
| PUBACK | PUBLISH ACKNOWLEDGMENT | /pʌb-æk/ | “帕布-艾克” |
| PUBREC | PUBLISH RECEIVED | /pʌb-rek/ | “帕布-瑞克” |
| PUBREL | PUBLISH RELEASE | /pʌb-rel/ | “帕布-瑞尔” |
| PUBCOMP | PUBLISH COMPLETE | /pʌb-kʌmp/ | “帕布-康普” |
发音要点:
- PUB 统一读 /pʌb/(和 public 开头一样),不要逐字母拼 P-U-B;
- PUBREC 结尾是 "rek",克音收尾;
- PUBREL 结尾是 "rel",类似 real 的发音;
- 行业习惯都是整体连读,直接说 "pub-rek"、"pub-rel" 大家都听得懂。
而且缩写本身就暗示了报文的作用,反过来帮助记忆握手流程:
- PUBREC = Publish RECeived,“我收到了你的发布”
- PUBREL = Publish RELease,“我释放了这个报文,流程可以结束了”
五、总结
回到开头的题目,核心考点就是区分三个 QoS 等级的语义:
- QoS 0 → 最多一次:只发一次,可能不到
- QoS 1 → 至少一次:没回音就重发,宁多勿少
- QoS 2 → 恰好一次:两轮握手 + Packet ID 去重,不丢不重但开销最大
选择题里最容易踩的坑是混淆“至少一次”(QoS 1)和“恰好一次”(QoS 2)。记住 QoS 2 的本质——用 PUBREC/PUBREL/PUBCOMP 三种额外报文把“交付”这个动作卡在流程最后,用 Packet ID 挡住重复——这道题就再也不会做错了。
实际选型时的原则也很简单:按业务容忍度选等级,而不是盲目追求最高 QoS。可靠性是用性能换来的,够用就好。