5月31日 00:26

MQTT QoS 0、1、2 有什么区别?实际项目该怎么选?

MQTT QoS 解决的是“消息交付可靠性和成本怎么平衡”的问题。QoS 0 是最多一次,速度最快但可能丢;QoS 1 是至少一次,能保证到达但可能重复;QoS 2 是恰好一次,流程最完整但开销也最大。实际项目里不是 QoS 越高越好,而是要看消息丢失、重复和延迟哪个代价更高。

三种 QoS 的核心区别

QoS 0 只有一条 PUBLISH,没有确认报文。发布者把消息发出去就算完成,网络抖动、客户端断开、Broker 繁忙都可能导致消息丢失。它适合高频遥测,比如温度、湿度、定位点,因为下一条数据很快会覆盖上一条。用 QoS 0 的好处是吞吐高、延迟低、设备耗电少。

QoS 1 会多一个 PUBACK。发布者发送 PUBLISH 后等待确认,如果没收到 PUBACK,就会重发。这样能提高送达概率,但接收方可能收到重复消息,所以业务处理必须幂等。比如告警消息、状态变更、日志上报一般可以用 QoS 1,但要给消息带上 messageId 或事件编号。

QoS 2 使用 PUBLISH、PUBREC、PUBREL、PUBCOMP 四步握手。它的目标是避免重复交付,适合不能重复执行的关键指令。问题是每条消息要更多报文和状态,延迟、内存和磁盘成本都会增加。很多系统口头说“必须恰好一次”,最后真正需要 QoS 2 的消息其实很少。

还要注意一个边界:MQTT 的 QoS 是客户端和 Broker 之间的交付保证,不等于你的业务端到端一定成功。Broker 收到了消息,不代表后端数据库写入成功;订阅者收到了消息,不代表业务处理完成。真正关键的业务还要在 payload 里设计业务流水号、状态机和补偿机制。

QoS 还会影响设备功耗和 Broker 资源。移动网络或卫星链路下,QoS 1 的重传可能让设备反复唤醒无线模块,电池消耗会明显上升。Broker 侧也要保存未确认消息,连接越多、离线越久,堆积风险越大。所以 QoS 选择应该按主题分级,而不是全局一刀切。

python
import paho.mqtt.client as mqtt client = mqtt.Client(client_id='device-001') client.connect('broker.example.com', 1883, 60) client.publish('device/001/event', '{"id":"evt-1001"}', qos=1) client.loop(timeout=1.0) client.disconnect()

一个比较稳妥的策略是按业务主题设置默认 QoS。状态心跳、实时位置用 QoS 0,告警、配置结果用 QoS 1,涉及扣费、开锁、停机这类强约束命令再评估 QoS 2。即便使用 QoS 2,也不要省掉业务回执,因为设备收到命令和执行成功是两件事。把这两层分清,问题定位会容易很多。

追问

QoS 1 为什么会重复?

QoS 1 的确认依赖 PUBACK,如果发布者发出了消息但没有收到确认,它只能假设消息没成功,然后重发。问题是接收方可能已经收到并处理了第一条,只是确认包在路上丢了。这个重复不是协议 bug,而是“至少一次”语义的必然代价。实际项目要用业务 ID 做去重,不能假设 QoS 1 永远只到一次。

QoS 2 能不能保证业务绝对不重复?

不能把协议层的恰好一次理解成业务层绝对不重复。QoS 2 主要保证 MQTT 报文交付过程不重复,但业务服务处理消息后可能重启、数据库提交可能超时、下游接口也可能重试。边界在于它管的是客户端到 Broker、Broker 到订阅者这段链路。关键业务仍然需要幂等写入和状态校验。

为什么很多物联网数据用 QoS 0?

传感器数据通常是连续上报的,单点丢失不会影响整体趋势。比如温度每 5 秒上报一次,丢一条比排队重传更能接受,因为旧数据很快失去价值。QoS 0 的优势是低延迟、低带宽、低功耗,适合电池设备。取舍是你要接受偶发丢包,并在服务端用时间窗口判断设备是否异常。

控制命令应该用 QoS 2 吗?

不一定。开灯、重启设备、下发配置这类命令更常用 QoS 1 加业务幂等,因为重复执行可以通过 commandId 防住。QoS 2 更适合重复执行会造成严重后果、且设备和 Broker 都能承受额外状态的场景。踩坑点是只提高 QoS,却没有处理命令超时、设备离线和执行回执。命令可靠性通常要靠 QoS、业务 ACK、超时重试一起完成。

订阅 QoS 和发布 QoS 不一样会怎样?

最终投递给订阅者的 QoS 通常取发布 QoS 和订阅 QoS 中较低的那个。比如发布者用 QoS 1,订阅者只订阅 QoS 0,Broker 会按 QoS 0 投递给它。这个规则容易被忽略,导致发布端以为消息可靠,消费端实际没有确认。排查时要同时看发布代码、订阅代码和 Broker 日志,不能只看一端配置。

标签:MQTT