3、QoS级别详解:QoS 0、1、2的定义、消息传递语义、适用场景与优缺点

3.1 核心原理:MQTT QoS 的本质是“消息送达保证等级”

MQTT 的 QoS(Quality of Service,服务质量)并非网络传输层的优先级标记,而是发布端(Publisher)与订阅端(Subscriber)之间,经由 Broker 中转时,对消息“是否送达、是否去重、是否有序”所达成的协议保证。它定义了消息在三个角色(发布端、Broker、订阅端)之间流转时,每一跳需要完成哪些确认与重试动作。

理解 QoS 的关键在于:QoS 是端到端的语义,但由逐跳(hop-by-hop)的机制实现。发布端与 Broker 之间、Broker 与订阅端之间,各自独立协商并执行 QoS 策略。例如,发布端以 QoS 1 发送消息给 Broker,Broker 可以以 QoS 0 转发给某个订阅端(取决于订阅时申请的 QoS 与 Broker 的策略)。因此,实际到达订阅端的服务质量,是发布 QoS 与订阅 QoS 两者中较低的那个等级。

记忆锚点:QoS 0 = “发了就不管”;QoS 1 = “至少一次,可能重复”;QoS 2 = “恰好一次,绝不重复”。这是三种等级最简练的概括。

下面这张图展示了三种 QoS 在发布端、Broker、订阅端之间的消息交互差异,重点突出确认报文(PUBACK、PUBREC、PUBREL、PUBCOMP)的流转路径:

发布端 Publisher Broker 消息代理 订阅端 Subscriber QoS 0 PUBLISH (无确认) 最多一次 QoS 0 PUBLISH (无确认) 直接转发 QoS 0 PUBLISH (无确认) 最多一次 QoS 1 PUBLISH PUBACK (需确认) 至少一次 QoS 1 PUBLISH PUBACK (需确认) 至少一次 QoS 1 PUBLISH PUBACK (需确认) 至少一次 QoS 2 PUBLISH PUBREC → PUBREL PUBCOMP QoS 2 PUBLISH PUBREC → PUBREL PUBCOMP QoS 2 PUBLISH PUBREC → PUBREL PUBCOMP 实线=消息流 虚线=确认流

3.2 三种 QoS 的详细消息传递语义

3.2.1 QoS 0:最多一次(At Most Once)

QoS 0 是默认等级,也是开销最小的等级。发布端发送一条 PUBLISH 报文后,不等待任何确认,不进行重试,Broker 收到后也不向订阅端确认。消息可能到达,也可能在网络中断或 Broker 异常时丢失。

  • 传递语义:消息可能丢失,但绝不会重复。
  • 协议行为:发送 PUBLISH 后立即丢弃本地缓存;Broker 收到后直接按主题匹配转发,不存储。
  • 性能特征:延迟最低,吞吐最高,无额外控制报文。

3.2.2 QoS 1:至少一次(At Least Once)

QoS 1 引入了确认与重试机制。发布端发送 PUBLISH 后,必须等待 Broker 返回 PUBACK 报文;若在超时时间内未收到 PUBACK,发布端会重新发送带有相同报文标识符(Packet ID)的 PUBLISH。Broker 在转发给订阅端时,同样需要等待订阅端的 PUBACK。

关键机制:QoS 1 保证消息“至少到达一次”,但不保证不重复。当 PUBACK 在网络中丢失时,发布端重发 PUBLISH,Broker 可能已经收到过该消息并完成转发,于是订阅端会收到两条相同的消息。因此,业务层需要具备幂等处理能力。

3.2.3 QoS 2:恰好一次(Exactly Once)

QoS 2 是最高等级,通过四段式握手确保消息不丢失且不重复。完整流程如下:

  1. 发布端发送 PUBLISH(包含 Packet ID)给 Broker;
  2. Broker 收到后,持久化该 Packet ID,返回 PUBREC(Publish Received)确认;
  3. 发布端收到 PUBREC 后,发送 PUBREL(Publish Release)释放报文;
  4. Broker 收到 PUBREL 后,删除该 Packet ID 的持久化记录,返回 PUBCOMP(Publish Complete),完成整个流程。

若任何一步报文丢失,发送方都会在超时后重发上一步的报文。由于 Broker 在收到 PUBLISH 后立即持久化 Packet ID,即使收到重复的 PUBLISH,也能识别并丢弃,从而保证订阅端只收到一条消息。

3.3 方法流程:如何选择 QoS 等级

选择 QoS 等级需要综合网络可靠性、数据重要性、业务容忍度、带宽成本四个维度。推荐按以下决策流程判断:

判断条件 推荐 QoS 理由
网络稳定(有线内网),数据可丢失,如传感器温度读数 QoS 0 降低开销,吞吐优先;丢失一帧数据不影响整体趋势
网络可能波动,数据重要但允许重复,如设备控制指令 QoS 1 保证送达;业务层通过指令 ID 去重
网络不可靠,数据极其重要且不允许重复,如支付指令、计费数据 QoS 2 端到端恰好一次;代价是吞吐量下降约 30%~50%
低功耗/弱网设备(NB-IoT、LoRa),电池供电 QoS 0 或 1 QoS 2 的多次握手显著增加功耗与流量

3.4 案例与场景分析

场景一:环境监测(推荐 QoS 0)

某智慧农业项目,每 10 秒上报一次土壤湿度。数据采集频率高、单帧丢失影响极小,且传感器电池续航要求高。此时使用 QoS 0,PUBLISH 报文仅约 30 字节,无任何确认开销,设备续航可延长 40% 以上。

# MQTT 客户端发布 QoS 0 示例(使用 mosquitto_pub)
mosquitto_pub -h broker.example.com -t farm/sensor/humidity \
  -m '{"value": 62.5, "unit": "%RH"}' -q 0

场景二:智能家居控制(推荐 QoS 1)

用户通过手机 App 远程关闭家中灯光。指令丢失会导致用户体验严重受损,但偶尔重复发送“关闭”指令不会造成安全问题(灯已经关了,再关一次无影响)。使用 QoS 1 并携带唯一的 command_id,服务端据此去重。

# 发布 QoS 1 消息,带命令 ID 用于幂等
mosquitto_pub -h broker.example.com -t home/livingroom/light \
  -m '{"cmd": "off", "cmd_id": "a3f9c2"}' -q 1

场景三:车联网计费(推荐 QoS 2)

新能源车充电桩上报每次充电的度数用于计费。消息重复会导致用户被多扣费,消息丢失会导致运营商损失。此时必须使用 QoS 2,确保每一笔计费记录恰好被处理一次。

# 发布 QoS 2 消息,确保计费数据不重不漏
mosquitto_pub -h broker.example.com -t charging/station/001/bill \
  -m '{"session": "S20250315_001", "kwh": 45.2}' -q 2

3.5 误区与复盘

常见误区一:认为 QoS 2 一定比 QoS 1 “更好”
QoS 2 的代价是吞吐量显著下降(实测约为 QoS 1 的 50%~70%),且 Broker 需要维护 Packet ID 状态表,内存占用更高。在不需要“恰好一次”语义的场景中使用 QoS 2,反而会拖垮系统性能。
常见误区二:忽略订阅端 QoS 的限制
即使发布端使用 QoS 2,如果订阅端在 SUBSCRIBE 报文中申请的是 QoS 0,那么 Broker 实际转发给该订阅端的消息等级是 min(发布 QoS, 订阅 QoS) = QoS 0。最终端到端保证可能远低于预期。
复盘建议:在系统设计阶段,应明确每条消息主题的“业务重要性等级”,并建立 QoS 选型映射表。上线前用弱网模拟工具(如 Clumsy、tc netem)验证不同 QoS 下的消息到达率与重复率,确保选型符合真实网络环境。