4、升级策略与分发机制:灰度发布、蓝绿部署、AB分区切换、断点续传

OTA升级做到最后,你会发现一个有意思的现象:技术实现只占一半,另一半是策略。代码写得再稳,推送策略一塌糊涂,照样翻车。我见过太多团队,升级包做好了,一键全量推出去,结果设备大面积变砖——说白了,就是没想清楚怎么发、发给谁、发错了怎么回滚。

这一章,咱们就把升级策略和分发机制掰开揉碎。灰度、蓝绿、AB分区、断点续传,这四个东西是OTA的四大护法。你想想看,没有它们,谁敢在千万级设备上做升级?

4.1 灰度发布:先放一小批,探探路

灰度发布,也叫金丝雀发布。名字挺文艺,做法很朴素:先让一小部分设备升级,观察没问题了,再逐步扩大范围。

我个人习惯把灰度分成三个阶段:

  1. 内测灰度(0.1% ~ 1%):只推给内部测试机、研发同事的设备。这个阶段主要看升级包能不能正常下载、解压、安装、启动。
  2. 公测灰度(5% ~ 20%):推给愿意尝鲜的种子用户。这个阶段要收集崩溃日志、性能数据、功能反馈。
  3. 全量发布(100%):确认没问题了,才放开所有设备。

关键指标:灰度期间必须实时监控三个数据——升级成功率(目标 > 99.5%)、崩溃率(目标 < 0.1%)、回滚率(目标 < 0.05%)。任何一个指标超标,立刻暂停灰度。

我曾经遇到过一件事:灰度到10%的时候,升级成功率掉到了97%。查了半天,发现是某个型号的Wi-Fi模块在下载大文件时会断流。还好只放了10%,要是全量推出去,那场面……嗯,你懂的。

避坑指南:灰度分组不要用设备ID取模,那样太死板。我建议用设备特征组合,比如「固件版本 + 地域 + 运营商」。这样灰度出来的数据更有代表性。

4.2 蓝绿部署:两套环境,随时切换

蓝绿部署的思路很简单:准备两套完全一样的环境,一套蓝(当前稳定版),一套绿(新版本)。升级的时候,把流量从蓝切到绿。出问题了?切回来就行。

在嵌入式设备上,蓝绿部署通常对应两个固件分区。设备启动时,从当前激活的分区启动。升级时,把新固件写到非激活分区,然后切换激活标志。

阶段 蓝分区(当前) 绿分区(新版本) 激活状态
升级前 v2.1.0(运行中) v2.0.0(旧版本) 蓝
写入中 v2.1.0(运行中) v2.2.0(写入中) 蓝
写入完成 v2.1.0(运行中) v2.2.0(待验证) 蓝
切换后 v2.1.0(备用) v2.2.0(运行中) 绿
回滚时 v2.1.0(运行中) v2.2.0(备用) 蓝

蓝绿部署最大的好处是回滚极快。只需要改一个标志位,重启设备,就回到旧版本了。不像全量升级,回滚还得重新下载旧包。

注意:蓝绿部署要求两个分区大小完全一致。我见过有人把新固件做得比旧固件大,结果写不进去,整个升级流程卡死。所以,分区大小一定要留余量,建议比预估固件大20%以上。

4.3 AB分区切换:双保险,永不落

AB分区切换,其实就是蓝绿部署在嵌入式领域的标准实现。A分区和B分区,一个运行,一个待命。升级时写待命分区,写完后校验,校验通过就切换。

这里有个细节很多人忽略:切换不是立即生效的。我习惯的做法是:

  1. 下载新固件到非激活分区
  2. 校验固件完整性(MD5/SHA256)
  3. 设置「切换计数器」为3
  4. 重启设备
  5. 设备从新分区启动,计数器减1
  6. 如果启动成功,清零计数器,锁定新分区
  7. 如果启动失败,计数器不为0,自动切回旧分区

为什么要设计数器?因为有些问题不是每次启动都复现的。给3次机会,能过滤掉大部分偶发故障。

核心原则:AB分区切换必须保证任何时候都有一个可启动的分区。写入过程中断电?没关系,旧分区还在。新分区启动失败?没关系,自动回滚。这才是真正的「永不落」。

我记得有一次,客户反馈设备升级后频繁重启。查了日志,发现是新版驱动在特定硬件版本上有兼容性问题。好在AB分区机制起了作用,设备在3次启动失败后自动切回了旧分区,用户几乎没感知到问题。

4.4 断点续传:大包不怕断

嵌入式设备的网络环境,说实话,挺恶劣的。Wi-Fi不稳定、4G信号差、用户突然移动……下载到一半断了,从头再来?用户不骂娘才怪。

断点续传的核心就一句话:记录已下载的字节范围,断连后从断点继续。

具体实现上,我推荐用HTTP Range头:

// 客户端请求:从第1048576字节开始下载
GET /firmware/v2.2.0.bin HTTP/1.1
Host: ota.example.com
Range: bytes=1048576-

// 服务器响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 1048576-4194303/4194304
Content-Length: 3145728

客户端需要维护一个下载状态文件,记录:

  • 固件总大小
  • 已下载大小
  • 每个数据块的MD5
  • 下载任务的唯一ID

断连后重新连接,先读取状态文件,然后从断点发起Range请求。这样即使断了10次,也只下载缺失的部分。

个人经验:我建议把固件分成1MB大小的块,每块单独校验。这样断点续传的粒度更细,而且可以并发下载多个块,提升速度。不过要注意,并发数不要超过4,否则嵌入式设备的CPU扛不住。

4.5 四种策略怎么选?一张图看懂

下面这张图,是我自己总结的决策流程。每次做OTA方案设计,我都会拿出来对照一下。

升级策略决策流程 开始设计升级策略 设备数量 > 1000台? 是 否 灰度发布 直接全量发布 需要快速回滚? 需要断点续传? 是 否 是 否 AB分区切换 蓝绿部署 断点续传 提示:四种策略可以组合使用 例如:灰度发布 + AB分区切换 + 断点续传,三管齐下最稳妥

4.6 组合策略:实战中的最佳实践

说实话,实际项目中很少只用一种策略。我通常的做法是三合一:

  • 灰度发布控制推送范围,降低风险
  • AB分区切换保证回滚能力,出了问题秒切
  • 断点续传提升用户体验,大包也不怕

举个例子,我之前做的一个智能家居项目,设备量50万台。升级策略是这样的:

  1. 先推给1000台内测设备(灰度1%)
  2. 观察24小时,确认无异常
  3. 扩大到5万台(灰度10%)
  4. 再观察48小时
  5. 全量推送,但每个设备下载时都支持断点续传
  6. 所有设备都采用AB分区,升级失败自动回滚

这个方案跑了两年,升级成功率稳定在99.8%以上。用户几乎感知不到升级过程——下载断了自动续,升级失败自动回滚,一切都在后台静默完成。

最后提醒一句:策略再完美,也要有人工干预的入口。我习惯在OTA后台加一个「紧急停止」按钮,一键暂停所有推送。这个按钮可能一年都用不上一次,但用上的那一次,能救你一命。