什么事情都没有做,为什么MQTT设备频繁收到相同消息
什么事情都没有做,为什么MQTT设备频繁收到相同消息
- 一、问题现象
- 二、排查过程
- 三、根因分析
- 3.1 同主题 + Retain = true 导致死循环
- 3.2 为什么指令类消息不能用 Retain
- 四、Retain(保留消息)机制说明
- 4.1 什么是 Retain
- 4.2 Retain 使用策略
- 五、修复建议
- 5.1 主题分离
- 5.2 Retain 配置调整
- 5.3 配置检查项
- 六、总结
一、问题现象
本人现在在做物联网智能设备开发,周末休息的时候,有客户使用产品,反馈 4G 开关产品存在以下异常:
- 设备一直在循环上报两条完整消息:
Topic: /tianyi-up-smartswitch1-866597078379797 QoS: 0 { "imei": "866597078379797", "iccid": "89861125208093419149", "online": 1, "version": "1.0.10" } Topic: /tianyi-up-smartswitch1-866597078379797 QoS: 0 { "success": true, "message": "ok", "imei": "866597078379797", "source": "command", "commandName": "controller-restart", "messageId": "" }- 上午下发软重启任务时,retain 默认成 true:客户当时未注意该配置,后发现此问题
二、排查过程
经逐步排查,从表象定位到根因:
| 排查步骤 | 发现 |
|---|---|
| 初步分析 | 设备循环上报在线状态和重启响应,消息重复收发 |
| 深入排查 | 发现发布主题与订阅主题使用同一主题(/tianyi-up-smartswitch1-866597078379797) |
| 进一步确认 | 上午下发软重启指令时,retain默认设为true |
| 根因定位 | 设备收到指令后回包,回包又被自己收到,形成消息死循环 |
三、根因分析
3.1 同主题 + Retain = true 导致死循环
客户(平台) Broker 设备 │ │ │ │── retain=true ──"controller-restart"────────→│ │ │ 发布到 /tianyi-up-smartswitch1-... │──→ 设备收到重启指令 │ │ │ 执行重启,回包 │ │ │←──"success:true..."────────────│ │←────────────────────────────────────────────│ 发布到同一主题! │ │ │ │ │ 客户收到响应 │ │ │ (如果也做响应...) │ │ │── 再次回包 ─────────────────────────────────→│ │ │ ↓ 无限循环 │ │核心问题:
- 发布和订阅使用同一主题(
/tianyi-up-smartswitch1-866597078379797) - 指令下发时
retain = true,回包也带retain = true - 设备收到自己的回包,误认为是新指令,再次响应并上报
3.2 为什么指令类消息不能用 Retain
| 问题 | 后果 |
|---|---|
| 指令重复执行 | 设备断网重连后,再次执行旧指令(如再次重启) |
| 时序混乱 | 新旧指令混在一起,无法区分 |
| 安全风险 | "开/关"等指令被保留,意外触发后果严重 |
| 消息循环 | 同主题下回包触发自我响应,无限循环上报 |
四、Retain(保留消息)机制说明
4.1 什么是 Retain
当发布消息时设置retain = true,Broker 会保存该主题的最后一条消息。之后任何新订阅者连接时,会立即收到这条保留消息,即使消息是在订阅之前发布的。
4.2 Retain 使用策略
| 消息类型 | Retain | 原因 |
|---|---|---|
| 控制指令(软重启、开/关、参数设置) | ❌false | 只执行一次,过期即失效 |
| 设备响应/回包 | ❌false | 避免循环触发 |
| 设备状态(在线/离线) | ✅true | 新订阅者需要知道当前状态 |
| 配置信息(阈值、工作模式) | ✅true | 设备上线立即获取最新配置 |
| 遥测数据(温度、电量) | ❌false | 实时数据,保留旧值无意义 |
五、修复建议
5.1 主题分离
建议将收发分离为两个主题:
| 用途 | 主题路径 | 说明 |
|---|---|---|
| 平台下发指令 | /tianyi-up-smartswitch1-{imei}/cmd | 平台发布,设备订阅 |
| 设备上报响应 | /tianyi-up-smartswitch1-{imei}/ack | 设备发布,平台订阅 |
平台下发指令 ──→ /tianyi-up-smartswitch1-866597078379797/cmd ──→ 设备订阅收到 设备回响应 ──→ /tianyi-up-smartswitch1-866597078379797/ack ──→ 平台订阅收到 ↑ 互不干扰,无循环主题分离后,即使同时订阅
cmd和ack两个主题,也不会产生循环。因为设备回包只发到ack,不会发到cmd。
5.2 Retain 配置调整
| 消息类型 | Retain 设置 | 建议 |
|---|---|---|
| 平台下发指令(软重启、开/关) | false | 发布时显式设置 |
| 设备响应/回包 | false | 设备端已默认 |
关键操作:发布任何控制指令时,显式设置retain = false。
5.3 配置检查项
| 序号 | 检查项 | 优先级 | 状态 |
|---|---|---|---|
| 1 | 下发指令时retain显式设为false | 🔴 高 | ☐ |
| 2 | 发布主题与订阅主题分离(cmd / ack) | 🔴 高 | ☐ |
| 3 | 消息增加唯一 ID(messageId),支持业务层去重 | 🟢 低 | ☐ |
六、总结
客户最初反馈"设备一直循环上报两条数据",经排查根因是上午下发软重启指令时
retain默认成true,且收发使用同一主题,导致设备回包被自身再次接收,形成消息死循环。建议通过主题分离及调整 retain 配置解决。
