mqtt Retained Message

MQTT 保留消息完整指南

一、保留消息是什么

保留消息是 MQTT 的一种机制。发布者在发布消息时,如果设置了 retain = true,Broker 就会为这个主题保存这条消息。之后任何新订阅这个主题的客户端,都会立刻收到这条保留消息。

它的核心作用是:让新订阅者不用等下一次发布,就能立刻拿到该主题的最新状态。

二、保留消息的核心规则

规则 说明
只存最后一条 同一个主题下,新的保留消息会覆盖旧的
新订阅者立刻收到 订阅时立即推送,不用等下一次发布
与订阅者无关 不管有没有人订阅,保留消息都一直存在
谁都能删 向该主题发布一条空 Payload 的保留消息,即可删除
由发布者决定 发布时设 retain = true 才会被保留

三、完整逻辑

发布者发布 retain=true 的消息 -> Broker 把这条消息存起来(每个主题只存最后一条) -> 新订阅者上线,订阅该主题 -> Broker 立刻把保留消息推给它。

四、触发条件:SUBSCRIBE,不是连接

保留消息的触发条件是 SUBSCRIBE(订阅动作),而不是连接本身。

根据 MQTT 3.1.1 规范 [MQTT-3.8.4-3]:如果服务端收到一个 SUBSCRIBE 报文,其主题过滤器与现有订阅的主题过滤器相同,它必须用新订阅完全替换现有订阅,并且必须重新发送匹配该主题过滤器的任何现有保留消息。

这意味着:即使客户端已经订阅了该主题,只要它再次发起 SUBSCRIBE,Broker 就会重新推送保留消息。

场景 会收到保留消息吗 说明
全新的订阅者(第一次订阅该主题) 会 订阅关系建立,立刻推送保留消息
已订阅者重新上线,重新发起 SUBSCRIBE 会 重新订阅会触发保留消息重新推送
已订阅者重新上线,没有发起 SUBSCRIBE 不会 没有新的订阅动作,不会重发保留消息

五、结合 Clean Session 的实际情况

Clean Session 管的是“会话状态是否保留”,保留消息管的是“订阅动作是否触发推送”。两者作用不同,不能简单地说“Clean Session=false 就不会收到保留消息”。

Clean Session 重连后是否重新订阅 会收到保留消息吗
false 恢复原订阅关系,通常不重新 SUBSCRIBE 通常不会,因为没有新的 SUBSCRIBE 动作
false 客户端代码主动重新 SUBSCRIBE 会,重新订阅会触发保留消息重新推送
true 旧会话被清空,必须重新 SUBSCRIBE 会

关键点:不是 Clean Session=false 本身导致收不到保留消息,而是因为客户端没有发起新的 SUBSCRIBE 动作。如果客户端在重连后主动重新订阅了主题,保留消息依然会被推送。

六、为什么“会话过期时间=0”也能收到消息

在 MQTTX 里,如果你把“会话过期时间”设为 0,理论上 Broker 在你断开后会立即销毁会话,离线消息不可能被保留。

但你依然收到了消息,原因通常是:

  • 你收到的是保留消息,不是离线消息。
  • 保留消息的触发条件是 SUBSCRIBE,只要你重连后重新订阅了主题,Broker 就会把该主题的最后一条保留消息推给你。
  • 保留消息和离线消息是两套独立的机制,保留消息不依赖持久会话。

七、保留消息 vs 遗嘱消息

对比项 保留消息 遗嘱消息
谁设置 发布者发布时设置 客户端连接时设置
谁发布 发布者自己发布 Broker 代为发布
触发条件 有客户端发起 SUBSCRIBE 客户端异常断开
是否一次性 否,一直保留到被覆盖或删除 是,发布后从会话中删除
能否被新订阅者收到 能,只要没被删除 只有设置了 Retain 才能

八、保留消息 vs QoS 1/2 离线消息

对比项 保留消息 QoS 1/2 离线消息
保留几条 每个主题只保留最后一条 离线期间的所有 QoS 1/2 消息
触发条件 SUBSCRIBE 订阅方重连后恢复会话
前提条件 发布时设 retain=true 订阅方使用持久会话,订阅 QoS > 0
适用场景 设备状态、最新配置 关键指令、需要补发的消息

九、MQTT 5.0 会话过期间隔的四种情况

MQTT 5.0 引入了 Session Expiry Interval,它和 Clean Start 组合,决定了会话在断开后的保留行为[citation:4]:

Clean Start 会话过期间隔 断开后行为
true 0 立即销毁会话
true 无穷大 立即销毁会话(cleanStart=true 优先)
false > 0 保留会话一段时间,期间重连可恢复,超时则销毁
false 无穷大 永久保留会话,重连可恢复

十、一句话总结

保留消息由发布者在发布时设置 retain = true,Broker 为每个主题存下最后一条。触发条件是 SUBSCRIBE,不是连接。Clean Session=false 时重连,如果客户端没有发起新的 SUBSCRIBE,通常不会收到保留消息;但如果客户端主动重新订阅,保留消息依然会被推送。会话过期时间=0 时收到的消息,大概率是保留消息,不是离线消息。

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容