设备在线不代表指令一定执行成功;MQTT QoS 1 的 PUBACK 也不等于阀门已经动作、继电器已经闭合。设备控制必须区分消息投递、业务受理与真实执行结果。
先区分三种确认
| 确认阶段 | 它能证明什么 | 它不能证明什么 |
|---|---|---|
| 消息层确认 | 消息按所选 QoS 在协议层被确认 | 设备动作已经完成 |
| 业务受理回执 | 设备进程已解析并接受该命令 | 执行器一定到达目标状态 |
| 执行结果/状态上报 | 设备报告了动作结果或可观测状态 | 所有物理异常都已排除 |
以 QoS 1 为例,消息可能重复到达,所以接收端必须允许重复消息而不重复产生危险动作。Broker 保证的投递语义不能直接替代设备业务侧的一致性机制。
一个最小但明确的命令协议
{
"command_id": "cmd-8f2a", // 示例 ID
"device_id": "valve-017",
"action": "set_position",
"target": 30,
"issued_at": "2026-10-25T12:00:00Z",
"expires_at": "2026-10-25T12:00:20Z"
}
这里的字段是示意,不代表任何现有设备协议。关键是 command_id、目标设备、允许动作、参数范围与过期策略;真实产品还需考虑签名、认证、重放防护和安全联锁。
状态机要显式,而不是用一个布尔值
created → queued → delivered → accepted → executing → succeeded
↘ failed
↘ timed_out
↘ rejected
断线重连 → reconcile(查询设备真实状态) → 更新最终状态
服务端应保存命令状态和最后的可信结果。设备接收命令时,先依据 command_id 去重:已执行则返回原结果;正在执行则报告处理中;未执行且未过期才进入动作路径。对需要严格执行一次的物理动作,还要考虑断电窗口、持久化写入和执行器本身的去重能力。
恢复策略与超时处理
- 设备离线:队列只保留仍然有效的命令;已过期的动作不可盲目补发。
- 网络抖动:使用指数退避与抖动控制重连,防止大量设备同时冲击服务端。
- 命令超时:标记结果“未知”而非直接断言失败,先查询当前设备状态再决定是否补发。
- 重启恢复:持久化必要的 command_id 与动作结果,恢复后先完成状态对账。
- 异常保护:对阀门、电机等执行器,遵循硬件安全约束和本地优先级,不把远端请求视为绝对指令。
PoC 测试比“能收发 MQTT”更重要
- 重复发送相同 command_id,确认设备不会重复执行非幂等动作。
- 模拟 Broker 重连、设备断电和报文延迟,确认状态能够恢复。
- 注入乱序回执,确认旧结果不会覆盖新的有效状态。
- 模拟执行器拒绝、到位失败与传感器异常,检查错误能否被清楚呈现。
- 通过日志和关联 ID 复盘“下发—受理—执行—回执”的完整链路。