第二章:OTA系统核心架构设计:整体架构分层

聊到OTA架构,我习惯先画一张图。一张从上到下的分层图。

为什么?因为OTA升级这件事,说白了就是「把新固件从云端送到终端,再安全地写进去」。这个链条上,每个环节的职责完全不同。混在一起设计,后期维护会让你想哭。

我在2018年做过一个智能门锁项目,当时团队把升级逻辑全塞在终端里。结果呢?云端换了个协议,终端代码要改一半。嗯,从那以后,我再也不敢轻视分层了。

2.1 三层架构总览

OTA系统,我建议分成三层:

  • 云端层:负责固件管理、策略下发、状态监控
  • 管道层:负责数据传输、协议适配、安全通道
  • 终端层:负责下载校验、分区切换、异常回滚

你想想看,这三层就像快递系统:云端是仓库,管道是运输车,终端是你家门口的收件箱。各管各的,但缺一不可。

核心原则:每一层只做自己的事,不要越界。云端不要管终端用什么Flash,终端不要管云端用什么数据库。

2.2 云端层:大脑与指挥中心

云端层的职责,我总结为四个字:管、控、看、算

  • :固件版本管理、增量包生成、灰度发布策略
  • :升级任务下发、设备分组、升级策略调度
  • :升级进度监控、失败原因统计、设备状态上报
  • :差分算法(bsdiff/hdiff)、签名验签、设备画像分析

这里有个坑。我曾经遇到一个客户,云端直接把100MB的全量包推给2G网络的设备。结果呢?下载到一半网络断了,设备变砖。所以云端一定要做策略适配——根据设备网络状况、电量、当前版本,决定推全量包还是增量包。

我的习惯:云端层一定要保留「升级任务ID」这个字段。每个升级任务生成唯一ID,后续所有日志、状态、回滚都基于这个ID追溯。别问我为什么,你排查线上问题时就知道了。

2.3 管道层:桥梁与安全卫士

管道层常被忽视。很多人觉得「不就是HTTP/HTTPS传个文件吗?」

其实没那么简单。

管道层要解决三个核心问题:

  1. 协议选择:MQTT用于指令下发,HTTP/HTTPS用于文件下载,CoAP用于低功耗设备。我建议不要只用一种协议。指令和文件传输分开,性能会好很多。
  2. 断点续传:终端下载到80%断了,难道重新下?当然不。管道层要支持Range请求,或者自定义分片协议。
  3. 安全传输:TLS是标配。但有些低功耗MCU跑不动TLS怎么办?我做过一个方案:用DTLS + 预共享密钥,既保证安全,又节省资源。

注意:管道层不要做「透传」就完事了。一定要加心跳检测和超时重试机制。我曾经见过一个项目,管道层没做超时,终端下载卡住三天,云端还以为在正常升级。

2.4 终端层:执行者与最后防线

终端层是OTA的最后一公里。这里出问题,前面做得再好都白搭。

终端层的核心职责:

  • 下载管理:分片下载、校验完整性、断点续传
  • 存储管理:双分区(A/B分区)或单分区+备份区
  • 升级执行:解压、校验签名、写入Flash
  • 异常处理:升级失败自动回滚、看门狗保护

我特别想强调一点:终端层一定要有「最后一道防线」。什么意思?就是不管云端发什么指令,终端自己要有校验逻辑。签名校验、版本号校验、硬件兼容性校验,一个都不能少。

举个例子:

// 终端升级前的自检逻辑
if (!verify_signature(firmware_data, signature)) {
    log_error("签名校验失败,拒绝升级");
    return OTA_FAIL_SIGNATURE;
}

if (firmware_version <= current_version) {
    log_error("版本号不合法,拒绝降级");
    return OTA_FAIL_VERSION;
}

if (!check_hardware_compat(firmware_data)) {
    log_error("固件不兼容当前硬件");
    return OTA_FAIL_HW_MISMATCH;
}

这段代码看着简单,但救过我的命。有一次云端配置错误,把旧版本固件推给了新设备。要不是终端做了版本号校验,那一批设备全得返厂。

2.5 关键设计原则

做OTA架构设计,我总结了五条原则。每一条都是用教训换来的。

原则 说明 我的经验
1. 幂等性 同一个升级任务执行多次,结果一样 终端要支持重复下载、重复校验,不会因为重复执行而出错
2. 可回滚 任何时候都能回到上一个稳定版本 A/B分区是最稳妥的方案,单分区要做好备份区
3. 最小影响 升级过程不影响设备核心功能 比如智能门锁升级时,不能影响开锁功能
4. 灰度发布 先小批量验证,再全量推送 我习惯先推1%的设备,观察24小时没问题再扩大
5. 可观测性 每个环节的状态都要能监控 日志要带上时间戳、设备ID、任务ID,方便排查

避坑指南:我曾经在一个项目中忽略了「幂等性」。终端下载到一半收到重复的升级指令,结果同时启动了两个下载任务,Flash写乱了。从那以后,我要求所有终端必须实现「升级状态机」——同一时刻只能有一个升级任务在执行。

2.6 架构设计的取舍

说实话,没有完美的架构。只有适合你的架构。

如果你做的是智能家居设备,终端资源有限,那云端层就要多做点——比如云端生成增量包、云端做策略调度。终端只管下载和写入。

如果你做的是工业设备,对可靠性要求极高,那终端层就要多做点——比如双分区、多重校验、本地日志。云端反而可以简单点。

我个人的建议是:先做减法,再做加法。第一版只实现核心链路:云端上传固件 → 终端下载 → 校验 → 写入 → 重启。跑通了,再慢慢加灰度发布、断点续传、增量升级这些高级功能。

嗯,架构设计就是这样。想清楚分层,定好原则,剩下的就是填代码了。

下一章,我会详细讲终端层的双分区设计。那是OTA最核心的技术细节,也是我踩坑最多的地方。