OpenHarmony 构建超时的重试与缓存机制
OpenHarmony 构建超时的重试与缓存机制
同一条流水线,昨天过、今天挂、重跑又过,日志里躺着几行
Read timed out。这到底是环境的锅,还是代码的锅?本文从一段构建日志讲起(地址与版本已占位化)。
一、先看一段构建日志
在一次 OpenHarmony 工程的构建日志里,有这么几行:
[T+0s] Failed to open https://<公共镜像源>/harmonyos/compiler/clang/<工具链版本>/llvm-<工具链版本>-release-x86_64.tar.xz,
Error: HTTPSConnectionPool(host='<公共镜像源>', port=443): Read timed out.
[T+1s] Failed to open https://<公共镜像源>/harmonyos/compiler/clang/<工具链版本>/llvm-<工具链版本>-release-ohos.tar.xz,
Error: HTTPSConnectionPool(host='<公共镜像源>', port=443): Read timed out.
[T+2s] Downloaded <WORKSPACE>/openharmony/_prebuilts/<32位hash>.llvm-<工具链版本>-release-x86_64.tar.xz
Start decompression ...
注意时间戳:T+0s 报超时,T+1s 又报超时,T+2s 就下载成功了。
也就是说,报错之后两秒,文件就到位了。这次构建最终是成功的。
但如果你只看日志里的 Error,很容易得出"构建挂了"的结论;如果你只看流水线红色状态,又很容易得出"环境不稳定,重跑就行"的结论。这两种结论都不准。 要判断准,得先知道构建为什么需要下载、下载失败会有几种形态。
二、构建为什么必须先联网
OpenHarmony 的编译不是"抄起源码就编"。在真正调用 GN / Ninja 之前,有一批预编译工具和第三方依赖必须从远端拉下来,典型包括:
- 编译工具链本身:clang / llvm、交叉编译用的 GCC;
- 构建系统:GN、Ninja;
- 语言运行时与包管理:Node.js、Python 及其依赖;
- 各芯片方案厂商 SDK 里的预编译库。
这些东西体积大、版本固定,官方不会放进代码仓,而是放在公共 mirror 上,由构建脚本按需下载。日志里那个下载脚本就是干这件事的——池化下载器,负责下载 + 解压 + 缓存。
所以"构建要联网"不是可选项。网络一旦抖动,构建就会以各种姿态报错。
三、下载类失败长什么样
同样是"下载挂了",日志里的措辞不一样,含义也不同:
| 日志片段 | 含义 |
|---|---|
Read timed out | 连上了,但读取数据超时。多半是网络慢或对端限速。通常可重试 |
Connection aborted | 连接中途被断开。可能是网络抖动、也可能是对端主动断流 |
Failed to resolve / Could not resolve host | DNS 解析失败,根本没连上 |
403 / 404 | 连上了但文件不可取——这通常不是网络问题,而是地址或权限配错 |
关键区别:前三种是"传输层的问题",第四种是"配置层的问题"。前者重试往往能过,后者重试一万次也不会好。
四、为什么"重跑就好"——重试与缓存
回到开头那段日志。"超时后 2 秒就成功"这件事说明了两点:
第一,池化下载器内置了重试。 一次 Read timed out 并不会立刻让整个构建失败,下载器会重新发起请求。所以你看到的是"Error 和 Downloaded 交替出现",而不是构建终止。
第二,下载结果会被缓存。 注意那行日志里的文件名:
_prebuilts/<32位hash>.llvm-<工具链版本>-release-x86_64.tar.xz
它前面那串 32 位十六进制串——是缓存的键(一般由下载地址算出),不是文件名本身。下载器用 hash 命名缓存文件,下次再要同一个地址,先查本地有没有这个键,命中就直接解压,不走网络。
这两点合起来,正好解释了"重跑就好"的直觉:
- 第一次跑:网络抖了一下,重试数轮后失败(或侥幸成功);
- 重跑:要么网络恢复了,要么大部分依赖已经躺在
_prebuilts/缓存里,需要联网的部分大幅减少。
所以"重跑就好"不是玄学,是重试 + 缓存这两个机制在起作用。
五、怎么判断是网络问题还是代码问题
这是最实际的一步。判断依据只有一个:失败发生在哪个阶段。
| 观察到的位置 | 归属 | 处理方向 |
|---|---|---|
| 失败在"下载 / 解压 / sync"阶段,且日志含 timed out / aborted / resolve | 网络/环境 | 重跑、检查镜像源与网络策略 |
失败在 ninja 编译阶段,报的是 error: 或 fatal error: | 代码/配置 | 按编译错误定位,重跑无用 |
| 失败在 GN 解析阶段 | 配置 | 查产品/部件配置 |
同一 URL 反复 403/404 | 配置 | 地址、版本号或权限有误,与网络无关 |
一个很实用的经验:如果日志里"网络类关键字"和"编译类错误"同时出现,先确认构建是在哪一步停的。下载阶段报的 Error 很可能只是重试过程的噪音,不该被当成根因。
换句话说:看到 Error 不等于构建失败;看到构建失败,再去看第一条真正的致命错误。
六、减少这类失败的做法
- 预热缓存:把
_prebuilts/这类下载缓存做成 CI 上的持久化目录,跨次构建复用,大幅降低联网依赖。 - 明确镜像源:统一走内部或公共镜像,例如包管理类依赖显式指定 registry,避免默认源不稳定导致长时间挂起。
- 区分"可重试"与"不可重试":下载类失败值得自动重试;编译类失败重试只会浪费机器时间。
- 不要用重跑掩盖真问题:如果同一个 URL 反复出错,那是配置问题,重跑只会一次次复现。
七、小结
- OpenHarmony 编译前需联网拉取预编译工具与依赖,"联网"是必经环节。
- 下载类失败按形态分四类:超时、连接中断、DNS 失败属传输层(可重试);403/404 属配置层(重试无用)。
- "重跑就好"的机制是重试 + hash 缓存,不是运气。
- 判断归属看失败阶段,不看日志里出现过多少
Error。 - 想让构建更稳,靠的是缓存预热与镜像源治理,而不是无脑重跑。
更多推荐
所有评论(0)