写这篇是因为这个机制前后改了四版,每一版都是被上一版的不足逼出来的。改动时间都对应着真实的版本发布,引用如下。
需求与约束
模块装在商用清洁设备上,干活的地方大量是地下车库、厂房边角——4G 弱网、短时断网是常态,不是意外。约束有三条:
- 断网期间数据不能丢。工时、清洁面积是客户的计费依据。
- 恢复之后数据要补得上。
- 恢复机制本身不能制造新故障。几百台设备在同一基站覆盖下,恢复策略设计不好就是重启风暴。
候选方案对比
| 方案 | 做法 | 结论 |
|---|---|---|
| A. 断网只重连,数据丢弃 | 最简单 | 计费数据丢失,直接否掉 |
| B. 固定间隔重试 + 超时重启 | 每 30s 重连一次,累计 N 分钟不通就重启 | 作为起步方案采纳,但留了隐患:固定间隔在断网恢复瞬间会撞车 |
| C. 阶梯退避 + 本地缓存 | 重连间隔逐级拉长,离线数据落外部 flash,恢复后补报 | 主方向 |
| D. 双卡 / 冗余网络 | 硬件冗余 | 成本和结构改动超约束,否掉 |
决策与理由(四版实录)
第一版(V0.1.11,2024-11):阶梯重连。 MQTT 重连间隔 2 → 4 → 8 … 60s 封顶;4G 注册状态断开 5 分钟重启模组、10 分钟重启整机;连接断开累计 10 分钟重启设备。这一版解决了"不断重试撞墙"的问题,数据缓存还没做。
第二版(V0.1.43,2025-01):分级恢复矩阵。 上线后发现弱网比预想的多,而且有一种新故障:模组假死——注册状态正常,实际不通。重试解决不了假死,只能重启。于是把恢复手段分级:持续离线 3 分钟重启 4G 模组(最多 10 次、间隔 5 分钟),10 次都进不了网重启整机;MQTT 应用层 15 分钟连不上服务器重启;拿不到云端账密持续 10 分钟重启;离线 1 分钟起把数据落 flash,每 10 分钟一批;再加一条每天凌晨 3 点的例行重启,兜底内存碎片。同一天的所有重启类改动一次发版。
第三版(V0.1.53,2025-02):修自己埋的雷。 第二版上线一个月,现场回报:设备凌晨 3 点会重启好几次。查下来是我们自己的 bug——例行重启的触发条件在重启完成后再次成立,于是重启循环。引入机制和修机制隔了 10 个小版本。教训记在复盘里:任何"定时主动重启"的逻辑,都要评审重启后的执行路径。
终版:指数退避 + 随机抖动。 固定阶梯有个理论缺口:断网恢复的那一瞬间,同一个基站下所有设备同时走到重连间隔的同一格,请求齐发,基站可能再次被打垮。给间隔乘一个随机抖动因子,把同步打散。这条成为简历上的最终口径。
验证与量产结果
- 数据完整性:离线 1 分钟起自动缓存到外部 flash(littlefs,掉电安全),恢复上线后按 10 分钟间隔补报;flash 容量内的断网时长,数据一条不丢。这是设计属性,不是概率性承诺。
- 假死覆盖:模组级重启上线后,原先必须人工现场断电的场景被自动恢复覆盖;整机重启作为最后一级,触发频率可接受。
- 已知数字缺口:弱网重连失败率的具体下降幅度,当时只留了定性记录,没有留存前后对比数据——这是本篇唯一补不上的数字。
遗留与下一版
凌晨 3 点的例行重启目前仍在服役(修完重复触发后它没再惹事),它兜底的是内存碎片。2026 年中的内存重构如果到位,这条兜底可以考虑退役——恢复机制的最终形态,应该没有一条重启是为了"以防万一"。