构建扫描与瀑布图:先搞懂这俩是什么
说实话,我第一次接触 Gradle 构建扫描时,心里是有点懵的。
当时项目编译越来越慢,从 2 分钟拖到 8 分钟,团队都在抱怨。我试了各种优化——改配置、加缓存、升级版本——效果都不明显。后来有个老同事说:「你跑一次构建扫描看看。」
好,那我们就从最基础的东西聊起。你理解了构建扫描和瀑布图,后面的优化才有的放矢。
什么是 Gradle 构建扫描
说白了,构建扫描就是 Gradle 在构建过程中记录的一份详细「体检报告」。它把每一步做了什么、花了多久、依赖了哪些文件、执行了哪些任务,全都存下来,然后在一个网页上展示给你看。
--scan 参数就行:
./gradlew build --scan
完成后终端会输出一个链接,点开就能看到完整的构建报告。
我个人习惯在 CI 上自动上传构建扫描,这样每次构建都能追溯。遇到问题直接拉出那次扫描,不用再问「你本地到底跑了什么」。我记得有次排查一个诡异的 task 顺序错误,就是靠扫描里的任务依赖图定位到的。
构建扫描里能看到的东西包括:
- 构建时间线:每个任务开始结束的时间戳,精确到毫秒。
- 任务依赖图:哪个任务依赖哪个,一目了然。
- 输入输出哈希:判断任务是否增量执行,有没有被缓存命中。
- 插件和脚本耗时:配置阶段和应用插件花了多少时间。
- 网络瀑布图:这是我们今天的重点,下面细讲。
网络瀑布图——任务执行的「时间轴」
在构建扫描的 Performance 页面里,有一个叫「Waterfall」的选项卡。很多人第一次看到它,会以为是网络请求的瀑布图。其实不一样——这里展示的是任务在 CPU 核心上的执行时间轴。
每一个横条代表一个任务的执行时段。横条越长,任务耗时越久。多个横条如果堆叠在一起,说明它们在并行执行;如果有明显的空隙,说明任务在「干等」——等前序任务完成。
核心价值:瀑布图能帮你一眼看出「哪些任务本该并行却串行了」「哪些任务在白白浪费 CPU 时间」。我遇到过一个坑:所有任务都在同一个线程里跑,瀑布图上看去全是一长串,没有任何重叠。后来发现是没开 --parallel 参数,加上后构建时间直接砍半。
举个例子,下面是一个简化的瀑布图结构:
从上图你能看到,瀑布图把构建扫描里的关键数据可视化成了时间条。每个任务就像一根小柱子,排在一起就知道谁先谁后、谁在等谁。
为什么需要瀑布图来诊断性能
光看构建扫描的汇总数字,比如「总耗时 6 分钟」,你很难下手。但一张瀑布图,能直击痛点。我把它总结为三个核心用途:
| 诊断维度 | 瀑布图表现 | 典型问题 |
|---|---|---|
| 并行度 | 任务条是否重叠 | 大量串行,未开启 --parallel 或 max-workers 设置过低 |
| 任务耗时 | 单个横条特别长 | 该任务内部有瓶颈(如编译、测试、打包) |
| 依赖链 | 空隙段(等待时间) | 前序任务太慢,下游任务被迫干等;或者配置阶段太长 |
举个例子。我参与过一个微服务项目,构建一次 15 分钟。我看瀑布图,发现所有模块的 compileJava、processResources、compileTestJava 全被排成了一串——明明都是独立子项目,结果一个接一个跑。后来在根 gradle.settings 里加了 parallel=true,并且每个子项目的 compileJava 任务之间没有任何实际依赖,直接让它们并行,构建时间降到 8 分钟。
注意:Gradle 默认是串行的(至少在 5.x 以前)。如果你没有显式配置并行,任务会乖乖排队。我曾经吃过一个大亏:一个 200+ 模块的项目,因为没开并行,CI 构建超时 30 分钟。开了 org.gradle.parallel=true 之后,直接降到 12 分钟。所以瀑布图第一眼看什么?看有没有大量非重叠的任务条。
如何快速上手生成扫描
咱们别光说不练。你可以在任何 Gradle 项目里试一下:
# 用 --scan 运行任意 task
./gradlew build --scan
# 或者你想让团队每次构建都自动上传
# 在 build.gradle 里加插件
plugins {
id 'com.gradle.build-scan' version '3.16'
}
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
}
}
扫描链接生成后,打开它。找到「Performance」→「Task Execution」→「Waterfall」。嗯,这就是我们的核心阵地了。
我个人习惯在本地开发时也用 --scan,特别是改动大配置之后。有一次我升级了 Gradle 8,觉得快了不少,但跑了一次扫描发现配置阶段反而慢了——原来是新版插件配置变更导致的。没有扫描,我永远不会注意到这 0.5 秒的退化。
小结
好了,这一章我们搞清楚了构建扫描是什么、瀑布图怎么来的。它们不是花架子,而是直接能帮你找到编译慢的「真凶」。你想想看,如果没有瀑布图,你只能对着总时间瞎猜,或者用 --profile 看一堆文本表格——效率差太多了。
下个知识点,我们准备深入瀑布图的坐标轴和色块含义。不过别急,先把基础打牢。今天就先练一下:在你自己的项目里跑一次 --scan,打开瀑布图,看看有没有明显的串行瓶颈。