持久会话中的MQTT QoS 2消息是否会被重复投递?
QoS 2消息的重复投递问题分析
之前通过Stack Overflow讨论纠正了对MQTT QoS 2的认知:QoS 2消息并非在所有场景下都能保证有序投递,比如消息属于不同主题、首次投递未得到正确确认时,就可能出现乱序。在此基础上,针对QoS 2消息是否会重复投递的问题,分析如下:
一、持久会话下的重复投递场景
正常流程(无重复)
QoS 2的交互流程为:
订阅者收到PUBREL消息后,会投递消息并向发布者发送PUBCOMP,同时在本地保存PUBREL及对应消息的处理记录。若PUBCOMP丢失,发布者再次发送PUBREL时,订阅者可通过本地记录识别出这是重复消息,不会重复投递。
异常场景(会出现重复)
若连接断开导致订阅者丢失了PUBREL相关信息及消息处理记录,即使重连后,订阅者也无法识别发布者重发的消息是否已处理,此时同一消息会被再次投递——这是实际存在的情况,并非误解。
二、其他导致重复投递的场景
- 订阅者会话状态完全丢失:比如订阅者进程崩溃、持久化存储损坏,导致之前的消息处理记录彻底丢失,重连后发布者重发的QoS 2消息会被当作新消息投递。
- 代理端状态异常:若代理在转发QoS 2消息时,自身保存的会话状态(如
PUBREC/PUBREL的交互记录)丢失,可能会重复向订阅者推送同一消息。 - 发布者重试逻辑异常:部分发布者实现若未正确维护QoS 2的重试状态,可能会在不必要的情况下重复发起消息投递。
三、MQTT规范的规定
MQTT 3.1.1及5.0规范中,QoS 2的定义是**“仅一次投递(Exactly Once Delivery)”**,但这个保证有前提:所有参与方(发布者、订阅者、代理)必须完整保存会话交互状态(如消息ID、处理记录等)。如果任何一方的状态记录丢失,规范并不保证避免重复投递——因为“仅一次”的实现完全依赖各方的状态匹配来完成去重。
四、不同MQTT实现的差异
- 主流实现(如EMQX、Mosquitto):严格遵循规范,在会话状态完整的前提下确保QoS 2消息仅投递一次;对于持久化会话,会将状态存储到磁盘,降低状态丢失的概率。
- 轻量级实现:部分轻量级客户端/代理可能仅在内存中保存状态,不做持久化,这类实现在进程重启、连接断开后,极易出现重复投递。
- MQTT 5.0扩展:部分实现会利用MQTT 5.0的会话过期时间、消息过期时间等特性,减少无效的重复投递,但核心仍依赖状态记录的完整性。
内容的提问来源于stack exchange,提问作者minghua
相关产品推荐
相关产品推荐

