HarmonyOS 7 MQTT:QoS1离线发件箱与会话续接
一、网络恢复了八条指令却发出了九次RelayMeter 是一个现场电表控制 Demo。页面上点“读取参数”“校准时钟”“切换采样档位”命令通过 MQTT over WSS 发给设备。实验室 Wi-Fi 稳定时一切正常到了地下配电间网络会在 5G、无网和弱 Wi-Fi 之间切换原本不显眼的状态问题一次暴露出来。19:42 的那轮测试里离线发件箱有 8 条指令其中 1 条已经过期网络恢复后应当发布 7 条并收到 7 个 PUBACK。实际日志却出现两次重复回执页面一度把已经完成的cmd-2048又标回“等待中”。原因不是 MQTT.js 不会重连而是应用同时启用了库的自动重连、Network Kit 的网络回调重连和页面onPageShow重连三个入口抢着改同一份状态。我把任务定为MQTT-1942固定客户端 IDmeter-7A19主题meters/7A19/commandsQoS 为 1会话过期时间 1800 秒。验收值也先写死重连 3 次sessionPresenttrue发件箱8 → 0过期 1发布 7PUBACK 7重复回执收敛 2最终状态必须是SYNCED。二、QoS 1 只保证至少一次不替业务决定“只能执行一次”MQTT.js 已经处理 PING、QoS 流程和重连但 QoS 1 的语义是至少一次。客户端没看到 PUBACK 时会重发Broker 或设备端就可能再次收到同一条命令。协议层的 packetId 也不能直接当业务幂等键重新建立会话、应用重启或队列迁移后它的生命周期和业务命令并不等价。所以 RelayMeter 在载荷里放稳定的commandId例如cmd-2048设备端按这个 ID 去重并回传业务回执。应用本地则维护AckLedger同一commandId的后续回执只更新观测计数不再逆转已经完成的 UI。PUBACK证明 Broker 接收了发布包业务回执证明设备处理了命令两者不能混成一个“成功”。Network Kit 在这里负责回答“当前是否有可用网络”MQTT.js 负责协议连接与会话ArkData 发件箱负责跨进程重启保存命令。三层各管一段页面只订阅聚合后的状态机OFFLINE → RECONNECTING → SESSION_RESUMED → DRAINING → SYNCED。三、重连入口只留一个退避节奏由协调器掌握第一个改动是关掉 MQTT.js 的固定周期自动重连把重连权交给MqttRecoveryCoordinator。这样 Network Kit 的netAvailable只发信号不直接创建第二个客户端。客户端 ID 必须稳定cleanfalseMQTT 5 会话过期时间设置为 1800 秒如果每次都随机 clientIdBroker 不可能返回旧会话。import{connectAsync,MqttClient}frommqtt;exportclassMqttRecoveryCoordinator{privateclient?:MqttClient;privategeneration0;asyncreconnect(url:string):Promisevoid{constgenthis.generation;awaitthis.client?.endAsync(true);constnextawaitconnectAsync(url,{clientId:meter-7A19,protocolVersion:5,clean:false,reconnectPeriod:0,keepalive:30,properties:{sessionExpiryInterval:1800}});if(gen!this.generation){awaitnext.endAsync(true);return;}this.clientnext;this.acceptConnack(next,gen);}}这里用generation隔离迟到连接。Network Kit 连续报告网络切换时旧 WSS 握手可能比新握手晚完成没有代次判断就会把页面重新绑到旧客户端。退避采用 1、2、4、8 秒并加少量抖动网络真正可用后才启动页面显示RECONNECTING但不会为每次网络属性变化都弹错误。生命周期也要成对处理。aboutToAppear注册 Network Kit 与 MQTT 事件aboutToDisappear解除页面观察者客户端由应用级协调器持有不随页面销毁就强制断开。只有账号退出、设备解绑或应用明确停用连接时才endAsync。把连接所有权放页面里会让一次返回键变成一次无意义的 Broker 重连。四、发件箱先落盘再谈 publish第二个问题是离线点击如何保存。MQTT.js 有 outgoing store但不同运行环境的持久化能力并不等于 HarmonyOS 应用级事务日志。RelayMeter 使用 ArkData 表保存commandId、topic、payload、createdAt、expiresAt、attempt 和状态。用户点击后先插入PENDING再由 drain 流程发布即使进程此时退出命令仍在。asyncdrain(now:number):Promisevoid{if(!this.client?.connected||this.draining)return;this.drainingtrue;try{constrowsawaitthis.outbox.listPending(meter-7A19);for(constrowofrows){if(row.expiresAtnow){awaitthis.outbox.markExpired(row.commandId);continue;}awaitthis.outbox.markInflight(row.commandId,row.attempt1);awaitthis.client.publishAsync(row.topic,row.payload,{qos:1,properties:{messageExpiryInterval:Math.ceil((row.expiresAt-now)/1000)}});awaitthis.outbox.markPubAcked(row.commandId);}}finally{this.drainingfalse;}}publishAsync返回后记录PUBACKED但不马上删除行因为设备业务回执可能稍后到达。真正完成后进入COMPLETED保留一个短期去重窗口再清理。过期命令不补发尤其“开闸”“停机”这类时效动作晚到比丢失更危险。批量 drain 也有上限本次每批最多 4 条避免网络刚恢复就把 7 条指令和订阅流量一起塞进链路。五、sessionPresent 不是一张“什么都不用做”的通行证cleanfalse且 Broker 保留了会话时CONNACK 的sessionPresent会是 true。它说明服务端找到了旧会话不代表本地发件箱已经和服务端完全一致。应用仍要对账本地INFLIGHT是否收到 PUBACK业务回执是否已落账订阅是否由旧会话保留。如果sessionPresentfalseRelayMeter 会重新订阅meters/7A19/acks并把INFLIGHT行退回PENDING让它们按照相同 commandId 再发如果为 true则保留 Broker 会话避免无意义重复订阅但仍扫描发件箱中没有完成证据的行。这个分支写在协调器不散落在页面按钮里。第三段代码处理业务回执。它用 commandId 做一次提交重复回执只增加duplicateAck指标不覆盖最终状态。asynconDeviceAck(payload:Uint8Array):Promisevoid{constackthis.codec.decodeAck(payload);constreservedawaitthis.ackLedger.reserve(ack.commandId);if(!reserved){this.metrics.duplicateAck1;return;}awaitthis.outbox.complete(ack.commandId,ack.deviceRevision);this.stateStore.commit({commandId:ack.commandId,status:COMPLETED,deviceRevision:ack.deviceRevision});}reserve必须和账本写入处于同一事务边界。先更新 UI、再写去重记录会在进程退出窗口留下“看起来成功、下次又执行”的缝隙。设备端也需要同样的幂等键客户端单边去重无法阻止命令被设备执行两次。六、日志要同时看网络、协议和业务三条线这次我没有只打印connectedtrue。HiLog 的第一行记录任务、clientId、QoS 和离线数量第二行记录reconnect3 sessionPresenttrue expiry1800s第三行给出outbox8 expired1 publish7第四行是puback7 duplicateAck2最后一行才宣布outbox0 stateSYNCED。网络线解释为什么开始重连协议线解释会话是否续接、PUBACK 是否齐全业务线解释设备回执是否重复、命令是否真正完成。三条线放到同一个任务 IDMQTT-1942下才不会把“网络恢复”误判成“设备状态已经同步”。七、最终页面把恢复过程留在屏幕上运行页顶部是稳定客户端meter-7A19和SESSION_RESUMED中间展示 Broker、主题、QoS、会话过期时间与当前网络下面是发件箱和回执统计。最终值为 Offline queued 8、Expired 1、Published 7、PUBACK 7、Duplicate ACK 2、Outbox 0。状态轨迹完整显示OFFLINE → RECONNECTING → SESSION_RESUMED → DRAINING → SYNCED。我特意保留“模拟断网”和“重新对账”两个按钮。前者只断开测试网络不清本地队列后者重新读取账本与 Broker 会话不生成新命令。19:42:08 的最终结果证明发件箱已经清空而不是靠刷新页面把数字藏掉。八、哪些事情不能交给这套 Demo 自动决定首先Broker 地址、认证令牌和 WSS 证书策略要按产品环境配置示例地址wss://iot.example.com/mqtt只是 Demo 标识。其次QoS 1 不提供业务事务语义涉及支付、门禁或高危设备时必须由服务端和设备端共同实现 commandId、权限、时效与审计。再次后台执行受系统策略约束不能因为使用 MQTT 就假设应用能永久保活。最后重连次数不是越多越可靠。真正稳定的实现是网络回调只唤醒一个协调器协调器只维护一个有效客户端发件箱只按证据推进状态重复回执只被观测而不反向污染 UI。把这几条边界守住后弱网恢复才从“碰巧连上了”变成可以复盘的工程过程。

相关新闻

【路径规划】基于matlab强化学习的改进蚁群算法RBIACO机器人路径规划【含Matlab源码 16026期】

【路径规划】基于matlab强化学习的改进蚁群算法RBIACO机器人路径规划【含Matlab源码 16026期】

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞&#x1f49…

2026/10/7 8:05:00 阅读更多 →
从单光子到星辰大海:欧洲超导单光子探测器项目如何助力量子通信与激光通信

从单光子到星辰大海:欧洲超导单光子探测器项目如何助力量子通信与激光通信

结论前置 超导纳米线单光子探测器(SNSPD)已成为量子通信与深空激光通信中不可替代的核心探测器件。欧洲正通过欧洲航天局(ESA)与欧盟委员会的多层级项目体系,系统性推进SNSPD从实验室原型向工业化产品的跨越。其中,Creotech Quantum主导的HRQKD项目(TRL 5)和Pixel Pho…

2026/10/7 8:05:00 阅读更多 →
2026 Java 后端面试风向变了:八股文只是门槛,场景化追问才是淘汰线

2026 Java 后端面试风向变了:八股文只是门槛,场景化追问才是淘汰线

2026 Java 后端面试风向变了:八股文只是门槛,场景化追问才是淘汰线 开篇先讲一个很具体的对比。同样是 JVM,同样是候选人,两种问法得到的是两种完全不同的人:问法期望答案考察的能力失分点“JVM 内存分为哪几块&#x…

2026/10/7 8:05:00 阅读更多 →

最新新闻

humanizer如何让改写不失真?SFT+DPO+GRPO训练流水线完整拆解(全程不用AI检测器)

humanizer如何让改写不失真?SFT+DPO+GRPO训练流水线完整拆解(全程不用AI检测器)

humanizer如何让改写不失真?SFTDPOGRPO训练流水线完整拆解(全程不用AI检测器) 【免费下载链接】humanizer 项目地址: https://ai.gitcode.com/hf_mirrors/jialinyyzz/humanizer humanizer 是一个 12B 参数的本地 AI 文本改写模型&…

2026/10/7 8:36:19 阅读更多 →
基于RAG技术的校园智能迎新咨询系统设计与实现

基于RAG技术的校园智能迎新咨询系统设计与实现

一、研究背景与意义每年9月迎新季是高校工作的高峰期。据教育部统计,2023年全国普通高校招生录取人数超过1000万人,新生入学咨询量呈爆发式增长。迎新期间,新生及家长关于报到流程、宿舍安排、缴费方式、校园设施、专业培养等问题的咨询量巨大…

2026/10/7 8:36:19 阅读更多 →
华为鸿蒙免费口算练习APP—小羊口算

华为鸿蒙免费口算练习APP—小羊口算

晚饭后孩子摊开口算本,写两道就开始磨铅笔。我去翻口算工具,不是先弹广告,就是把题锁在会员后面——手刚伸出去,兴致就没了。我想做一款打开就能练的:对错马上看见;练到一半被叫去洗碗,回来还能…

2026/10/7 8:36:19 阅读更多 →
WeXPort 微信聊天记录导出攻略指南:6 类职业场景一次讲清

WeXPort 微信聊天记录导出攻略指南:6 类职业场景一次讲清

前言微信聊天记录,对不同职业的人来说,价值完全不一样。微商靠它留客户承诺,法务靠它当证据,财务靠它对账,HR 靠它做交接,内容创作者靠它攒素材,普通打工人靠它留工作记录。但微信记录有一个特点…

2026/10/7 8:36:19 阅读更多 →
好靶场:逻辑漏洞-越权漏洞

好靶场:逻辑漏洞-越权漏洞

⚠️ 免责声明:本文所有操作仅在【好靶场】官方授权的线上安全练习靶场中完成,仅用于网络安全原理学习、技术研究。 严禁将文中Payload、漏洞利用方法应用到任何未获得授权的公网服务器、网站。 未经授权对网站进行渗透测试属于违法行为,一切…

2026/10/7 8:36:19 阅读更多 →
铝电解电容选型设计规范

铝电解电容选型设计规范

前言文档版本:V1.0编制目的:规范铝电解电容器选型、电路设计、降额设计、PCB布局及可靠性验证,明确寿命、纹波电流、温度、反压等关键限制,规避鼓包漏液、容量衰减、ESR上升、爆炸失效等风险。适用范围:直插有极性铝电…

2026/10/7 8:35:19 阅读更多 →

日新闻

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

ROS2机械臂仿真与运动控制:从URDF建模到Gazebo实战全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:01:58 阅读更多 →
用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

用浏览器直接改ESP32的WiFi密码:NVS键值配置工具设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →
芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

芯片封装缺陷检测:扫描声学显微镜(SAT)原理与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:02:00 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 7:15:40 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 5:29:09 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 6:26:51 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 8:21:32 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 4:21:51 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 1:18:13 阅读更多 →