集团站切换校区

验证码已发送,请查收短信

复制成功
微信号:togogoi
添加微信好友, 详细了解课程
已复制成功,如果自动跳转微信失败,请前往微信添加好友
打开微信
图标

学习文章

华为认证备考实践:MQTT 深度解析——发布/订阅模型与三级 QoS 的可靠投递机制

发布时间: 2026-10-29 13:25:57

MQTT协议

从发布订阅语义到三次握手,说清三档可靠性契约各自保证什么、代价几何

一、背景:为什么物联网最终选了 MQTT

​HTTP 的请求-响应模型在物联网场景里有两个硬伤:服务端无法主动下发指令,且每次请求都要携带完整头部,在 2G / NB-IoT 这类窄带链路上开销过高。MQTT 用长连接加发布订阅换掉了这两个问题——设备与 Broker 之间只维持一条 TCP 连接,消息按主题路由,最小固定头部可以压到 2 字节。

更关键的是,它把可靠性做成了可选档位。传感器上报温度丢一条无所谓,而远程开锁的指令重复执行一次就是事故。MQTT 允许同一套协议承载这两种截然不同的诉求,这正是它在工业与消费物联网里同时成为默认选项的原因。

二、核心概念

​在讨论 QoS 之前,有两个更基础的问题必须先讲清楚:消息究竟靠什么被路由到接收方,以及一条长连接是如何被确认仍然活着的。这两点决定了后面所有可靠性机制的边界。

(一)发布 / 订阅与主题​

MQTT 里没有"收件人",只有主题(Topic)。发布者把消息丢到 factory/line1/temp,Broker 负责把它推送给所有订阅了该主题的客户端。主题以 / 分层,支持 +(单层通配)与 #(多层通配):factory/+/temp 能匹配所有产线,factory/# 能匹配整个工厂。​

订阅关系天然解耦——发布者不需要知道有几个订阅者,订阅者掉线也不会影响发布者。

(二)会话与 Keep Alive

​客户端连接时使用 clean_start=False 并固定 client_id,Broker 就会为它保留订阅关系与未确认消息,这条特性是"离线消息"能力的前提。Keep Alive 则用于探测半开连接:客户端在 1.5 倍周期内未发出心跳,Broker 即判定其离线。​

三、三级 QoS:分别保证了什么​

QoS​
语义​
握手过程​
代价​
0最多一次​
无​
可能丢失,不重传​
1至少一次​
PUBLISH → PUBACK​
可能重复​
2恰好一次​
PUBLISH → PUBREC → PUBREL → PUBCOMP​
两次往返,内存开销最大​

有一个容易被忽略的细节:QoS 由发布侧与订阅侧各自协商。发布者以 QoS 2 发送、订阅者却以 QoS 1 订阅时,最终生效的是两者中的较小值。因此整条链路的可靠性由最低的那一环决定。

​四、实战:用 Python 验证行为差异

​以 paho-mqtt 2.1 为例(Python 3.11+,Broker 使用 Mosquitto 2.0):

# publisher.py —— 逐级发送同一条消息 
import paho.mqtt.client as mqtt 
 
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="pub-1") 
client.connect("localhost", 1883, keepalive=60) 
client.loop_start() 
 
for qos in (0, 1, 2): 
 info = client.publish(f"demo/qos{qos}", f"payload-qos{qos}", qos=qos) 
 # QoS 0 时 is_published() 立即为 True;QoS 1/2 需等握手完成 
 print(qos, "published:", info.is_published())
把订阅端断网 5 秒再重连,可以观察到清晰的分野:QoS 0 在断线期间的消息永久丢失;QoS 1 会在重连后补发,但可能收到两条重复;QoS 2 不丢不重,代价是 Broker 要为每条消息维护状态直到 PUBCOMP。

​五、原理深挖:重复与状态从哪来

​QoS 1 的重复源于"确认丢失"。Broker 收到消息后回 PUBACK,如果这个 ACK 在链路上丢了,发布者会重发同一条消息——它并不知道重复,其设计取向是宁可重复也不能丢。订阅侧同理。
QoS 2 用四次握手消除歧义:PUBREC 让发布者确认"Broker 已收到",但此时 Broker 尚不能投递;直到收到 PUBREL,Broker 才认定"对方不会再重发同一条",随后投递并回 PUBCOMP。代价是每条在途消息在 Broker 上至少占一份状态,高并发下这是实打实的内存压力。

​六、踩坑记录

  1. QoS 2 不是万能药。 它只保证 MQTT 层不重复,应用层重试引发的重复它管不着,幂等键仍然必须做。​
  2. QoS 1 的重复是常态而非异常。 下游处理必须幂等,不要指望靠计数来对账。
  3. 不要全链路开 QoS 2。 设备到云、云到设备通常用 1,仅在涉及金钱或安全的指令上才升到 2。

​七、性能与容量:QoS 的真实开销

​QoS 的代价并不是线性的。在同一台 Broker 上,QoS 1 的吞吐大致是 QoS 0 的 70% 到 85%(具体取决于网络 RTT),而 QoS 2 往往只剩 QoS 0 的一半左右——每条消息都要经历两次往返外加状态落盘。​

更值得警惕的是在线客户端数量对内存的放大效应。QoS 2 会话中每条未确认消息都要按客户端维度持有状态;若同时在线十万台设备、每台积压十条未确认消息,Broker 持有的状态对象就是百万量级。这正是许多生产环境最终把 QoS 2 限制在少数关键主题上的直接原因。​

​另一个常被忽略的变量是持久化会话带来的消息积压。开启 clean_start=False 后,Broker 会为离线客户端缓存消息;如果客户端长期不回来,积压会持续占用内存或磁盘。上线前务必为每个会话设置积压上限(如 Mosquitto 的 max_queued_messages、EMQX 的 max_mqueue_len),否则一次设备批量离线就足以拖垮整个集群。​

八、总结

选 QoS 的判断顺序是:先问"丢一条的后果",再问"重复一条的后果"。两个后果都能接受就选 0;怕丢不怕重选 1;两者都不能接受才选 2,并接受它的吞吐代价。​

上一篇: 人工智能算法学习:线性回归从数学原理到工程落地全解析

下一篇: 华为认证备考实践:边缘网关协议转换与本地数据处理

二十多年老品牌
微信咨询:togogo_hcie 咨询电话:18924184114 咨询网站客服:在线客服
在线咨询 ×

您好,请问有什么可以帮您?我们将竭诚提供最优质服务!