首页 / 资讯中心 / 文章详情

物联网笔记——ESP32-QoS笔记

物联网笔记——ESP32-QoS笔记 ★ FEATURED ARTICLE
MQTT QoS 0 / 1 / 2 学习笔记1. QoS 是什么QoS 全称Quality of Service服务质量等级它决定 MQTT 消息传输时需要保证到什么程度。MQTT 一共有三个 QoS 等级QoS 0At most once QoS 1At least once QoS 2Exactly once可以简单理解为QoS 0最多一次 QoS 1至少一次 QoS 2恰好一次2. QoS 02.1 工作流程QoS 0 最简单ESP32 Broker PUBLISH --------------------------发送完成后不等待 MQTT 层的发布确认。没有PUBACK因此QoS 0 发送一次 不等待 MQTT ACK 不进行 MQTT 层确认2.2 特点优点通信开销小 速度快 实现简单缺点消息可能丢失因此适合温度 湿度 光照 RSSI 实时位置 高频状态数据这些数据通常会持续刷新偶尔丢失一条影响不大。2.3 注意QoS 0 不等于完全没有 ACKMQTT 通常运行在MQTT ↓ TCP ↓ IPQoS 0 没有的是MQTT 层 PUBACK但是 TCP 自己仍然存在TCP ACK TCP 重传 数据排序 流量控制因此QoS 0 MQTT PUBACK ❌ TCP ACK ✅不能把 MQTT ACK 和 TCP ACK 混在一起。3. ESP-IDF 中的 QoS 0ESP-IDF 发布函数esp_mqtt_client_publish(client,topic,data,len,qos,retain);例如intmsg_idesp_mqtt_client_publish(event-client,esp32/test,hello esp32,0,0,0);其中0// QoS表示QoS 0实际实验中publish msg_id: 0这是正常现象。QoS 0 不需要使用 Packet Identifier 来跟踪后续 PUBACK因此 ESP-IDF 中 QoS 0 发布的msg_id为 0。4. QoS 1QoS 1 的目标是At least once 至少一次也就是说尽量确保消息不会因为一次传输失败直接消失。4.1 QoS 1 工作流程ESP32 Broker PUBLISH msg_id 20019 -------------------------- PUBACK msg_id 20019 --------------------------QoS 1 比 QoS 0 多了PUBACK即Publish Acknowledgement 发布确认5. msg_id 的作用例如 ESP32 发布msg_id 20019Broker 返回PUBACK msg_id 20019这样 ESP32 就知道Broker 确认的是 20019 这条消息。如果同时发送消息A → msg_id 100 消息B → msg_id 101 消息C → msg_id 102Broker 返回PUBACK 101ESP32 就知道101 已确认而不是其他消息。所以msg_id的重要作用就是发送消息 ↓ 分配 msg_id ↓ 等待 ACK ↓ 通过相同 msg_id 匹配确认6. ESP-IDF 中实际验证 QoS 1将qos0修改成qos1例如intmsg_idesp_mqtt_client_publish(event-client,esp32/test,hello esp32,0,1,0);实际运行得到publish msg_id: 20019说明 QoS 1 已经获得实际的消息 ID。7. MQTT_EVENT_PUBLISHED为了确认 Broker 是否真的确认消息可以增加elseif(event_idMQTT_EVENT_PUBLISHED){esp_mqtt_event_handle_tevent(esp_mqtt_event_handle_t)event_data;printf(MQTT Publish ACK, msg_id:%d\n,event-msg_id);}实际运行publish msg_id: 20019 MQTT Publish ACK, msg_id:20019两个 ID 一样20019 20019说明ESP32 ↓ PUBLISH 20019 ↓ Broker ↓ PUBACK 20019 ↓ ESP-IDF ↓ MQTT_EVENT_PUBLISHED因此MQTT_EVENT_PUBLISHED可以理解为对于这条 QoS 1 消息Broker 已经完成 MQTT 层确认。8. MQTT ACK 不等于设备执行成功这是工程中很重要的一点。假设发送{cmd:motor_stop}收到 MQTT ACK只能证明Broker 已确认 MQTT 消息不能证明电机真的停止了如果业务要求确认设备动作则应该服务器 ↓ MQTT CMD ↓ ESP32 ↓ 控制硬件 ↓ 读取实际状态 ↓ MQTT STATE ↓ 服务器也就是通信确认 ≠ 设备动作确认9. 为什么 QoS 1 还可能重复假设ESP32 Broker PUBLISH 20019 -------------------------- 已收到Broker 已收到消息并准备发送PUBACK 20019但是网络出现问题PUBACK 20019 ------------- X此时Broker我已经收到 20019ESP32我没收到 20019 的 PUBACKESP32不能确定消息是否到达因此可能重新发送。于是第一次 PUBLISH 20019 第二次 PUBLISH 20019所以 Broker 一侧可能遇到重复消息。因此QoS 1 至少一次不是恰好一次10. DUP 重发标志MQTT 的 PUBLISH 报文中存在DUP即Duplicate 重复发送第一次发送DUP 0需要重发时DUP 1可以理解为第一次 PUBLISH QoS 1 DUP 0 msg_id 20019重发PUBLISH QoS 1 DUP 1 msg_id 2001911. 幂等性因为 QoS 1 可能重复所以 IoT 控制命令最好具有幂等性含义同一个操作执行一次和执行多次最终结果相同。例如gpio_set_level(GPIO_LED,1);执行1次 → LED ON 10次 → LED ON最终状态一样。这就是幂等。推荐设计推荐set_led ON重复ON ON ON结果仍然ON不推荐toggle_led重复两次OFF → ON ON → OFF最后状态完全不同。所以实际 IoT 系统中设置目标状态通常比执行一次翻转更加适合作为远程控制命令。12. QoS 2QoS 2 的目标是Exactly once 恰好一次主要解决 QoS 1 可能重复的问题。QoS 2 是 MQTT 中最高的服务质量等级。13. QoS 2 四步流程完整流程ESP32 Broker PUBLISH -------------------------- PUBREC -------------------------- PUBREL -------------------------- PUBCOMP --------------------------四个阶段PUBLISH PUBREC Publish Received PUBREL Publish Release PUBCOMP Publish Complete14. QoS 2 为什么需要这么复杂假设msg_id 23984首先发送PUBLISH 23984Broker 收到后记录 23984 已收到然后返回PUBREC 23984即我已经收到并记录这条消息。ESP32 收到 PUBREC 后不再继续重复发送原始 PUBLISH而是进入下一阶段PUBREL 23984Broker 完成处理以后PUBCOMP 23984整个事务才结束。15. QoS 2 的核心思想可以理解为两个阶段。第一阶段你真的收到并记住了吗 ↓ PUBREC第二阶段既然已经记住 现在完成这次消息处理。 ↓ PUBREL ↓ PUBCOMP因此 Broker 即使再次收到相同 QoS 2 消息也能够根据保存的状态识别这是之前的消息而不是再次作为新的应用消息处理。16. ESP-IDF 中使用 QoS 2只需要修改qos2例如intmsg_idesp_mqtt_client_publish(event-client,esp32/test,hello esp32,0,2,0);不需要自己实现PUBREC PUBREL PUBCOMP这些 MQTT 协议流程由ESP-MQTT内部处理。程序仍然通过MQTT_EVENT_PUBLISHED知道这次发布流程已经完成。实际实验结果publish msg_id: 23984 MQTT Publish ACK, msg_id:23984说明此次 QoS 2 发布正常完成。17. QoS 0 / 1 / 2 对比QoS含义确认机制是否可能丢是否可能重复开销QoS 0最多一次无 MQTT PUBACK是一般不会因 MQTT 重传产生重复最低QoS 1至少一次PUBACK尽量避免是中QoS 2恰好一次PUBREC → PUBREL → PUBCOMP尽量避免MQTT 协议层避免重复交付最高18. 常见选择方法高频传感器数据例如temperature humidity light RSSI通常可以考虑QoS 0原因下一条数据很快就会刷新 偶尔丢失影响较小普通控制命令例如LED ON Relay OFF Set temperature 26通常可以考虑QoS 1同时使用幂等命令 必要时增加 command_id 业务层去重QoS 1 在很多 IoT 场景中是可靠性和开销之间比较常见的折中方案。对重复非常敏感的 MQTT 消息才考虑QoS 2因为通信次数更多 协议状态更多 资源开销更大所以QoS 并不是越高越好而是根据业务可靠性要求选择。19. 当前 ESP32 代码中的两个 QoS发布esp_mqtt_client_publish(event-client,esp32/test,hello esp32,0,1,0);这里QoS 1控制的是ESP32 发布esp32/test消息时使用什么 QoS。订阅esp_mqtt_client_subscribe(event-client,esp32/led,0);这里最后的0同样代表 QoS。表示ESP32 对esp32/led这个订阅请求使用 QoS 0。因此发布 QoS 和订阅 QoS 要分开理解。20. 本节必须记住的内容第一条QoS 0 最多一次 没有 MQTT PUBACK第二条QoS 1 至少一次 PUBLISH PUBACK 可能重复第三条QoS 2 恰好一次 PUBLISH PUBREC PUBREL PUBCOMP第四条msg_id 用于对应 MQTT 消息和确认流程第五条MQTT_EVENT_PUBLISHED QoS 1 / QoS 2 发布已经得到相应协议确认并完成第六条QoS 1 可能重复 → 控制命令尽量设计成幂等第七条MQTT ACK ≠ 设备实际动作完成第八条QoS 不是越高越好 而是可靠性、实时性、网络开销之间的权衡
阅读完成 · 觉得有帮助?
咨询建站