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 hostDNS 解析失败,根本没连上
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。
  • 想让构建更稳,靠的是缓存预热与镜像源治理,而不是无脑重跑。
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐