4. Gradle 缓存同步配置:settings.gradle 中的 buildCache
多模块、多分支、多人协作的大型项目,构建产生的大量中间产物(class 文件、jar、native 库)重复编译会导致构建时间指数级增长。Gradle buildCache 允许将任务输出物持久化到本地或远程存储,后续构建只需从缓存拉取,无需重新执行任务。本章深入 settings.gradle 中的 buildCache 配置块,覆盖远程缓存 URL 与凭据管理、启用/禁用策略,以及生产环境下的调试与优化。
4.1 buildCache 配置模型
buildCache 配置以 DSL 块形式出现在 settings.gradle(或 settings.gradle.kts)中。核心构件包括:
- local —— 默认启用,基于
~/.gradle/caches/build-cache-1目录。 - remote —— 可选,通过 URL 指向远程缓存服务(HTTP 或 Gradle Enterprise 兼容端点)。
- enabled / disabled —— 策略级开关,可对 local 和 remote 分别控制。
- push —— 远程缓存是否允许上传新缓存项。默认仅本地和远程都允许 push。
原理:每个 Task 的 输入哈希(input hash) 决定缓存键。缓存命中时直接反序列化输出物到 build/ 目录;未命中则执行任务并将结果 push 到所有启用了 push 的缓存后端。
4.2 settings.gradle 配置示例
以下为完整的 settings.gradle.kts 配置(Kotlin DSL 版本,Groovy DSL 语法类似):
buildCache {
// 本地缓存默认启用,但可调整目录大小
local {
enabled = true
// 最大缓存容量,单位字节(默认 10GB)
maxCacheSize = "20 GB"
// 缓存项保留时间(默认 7 天)
removeUnusedEntriesAfterDays = 14
}
// 配置远程缓存
remote(HttpBuildCache::class.java) {
url = uri("https://gradle-cache.team.example.com/cache")
// 基本认证凭据
credentials {
username = "cache-reader"
password = project.findProperty("cachePassword") as String?
}
// 启用/禁用策略
isEnabled = true
// 是否允许上传新缓存项
isPush = false // 多数场景下只从远程读取
}
}
若使用 Groovy DSL,等价写法为:
buildCache {
local {
enabled true
maxCacheSize = '20 GB'
removeUnusedEntriesAfterDays = 14
}
remote(HttpBuildCache) {
url = 'https://gradle-cache.team.example.com/cache'
credentials {
username = 'cache-reader'
password = project.findProperty('cachePassword')
}
enabled = true
push = false
}
}
4.3 启用/禁用策略详解
| 策略 | DSL 属性 | 作用域 | 说明 |
|---|---|---|---|
| 全面禁用 | buildCache { isEnabled = false } | 全局 | 所有缓存(local + remote)停用,构建完全不使用缓存。 |
| 仅本地 | remote { isEnabled = false } 或移除 remote 块 | 远程 | 仅在本地缓存中读写,常用于离线环境。 |
| 只读远程 | remote { isPush = false } | 远程 | 拉取远程缓存,但不上传新项,避免污染公共缓存。 |
| CI 只写 | remote { isEnabled = true; isPush = true },本地禁用 | 远程 | CI 任务编译后将结果上传到远程,开发者仅从远程拉取。 |
| 按项目条件 | 利用 gradle.startParameter.isBuildCacheEnabled | 运行时 | 在 buildCache 块内判断是否启用,例如 if (!gradle.startParameter.isBuildCacheEnabled) return。 |
推荐 CI 策略:CI 节点启用远程缓存 push,开发机只读远程 pull。通过 isPush = false 避免本地开发机意外上传重复或不完整的缓存项。
4.4 远程缓存 URL 与凭据管理
4.4.1 URL 格式
buildCache 的 url 指向一个 HTTP/HTTPS 端点,该端点必须实现 Gradle 缓存协议。常见后端:
- Gradle Enterprise / Develocity 内置缓存
- Nginx + 自定义缓存存储(基于 S3/MinIO)
- 专用缓存服务器(如
gradle-cache-backend开源方案)
4.4.2 凭据注入方式
避免在版本库中硬编码密码,推荐以下方式:
- Gradle 属性文件:在
~/.gradle/gradle.properties或 CI 环境变量中设置cachePassword=xxx。 - 命令行参数:
-PcachePassword=xxx传递。 - 文件系统凭据:Gradle 7.6+ 支持
HttpBuildCache.credentials { file = uri("file:///etc/gradle-cache-credentials") }读取文件内容。
安全提示:不要在 settings.gradle 中直接硬编码密码!若使用文件凭据,确保文件权限为 600,仅 Gradle 进程可读。
4.5 调试缓存行为
4.5.1 启动日志
在 gradle.properties 或命令行添加:
org.gradle.caching.debug=true
构建日志会输出每条任务的缓存查找、命中、存储信息:
CompileJava: cache hit, entry key=abc123
ProcessResources: cache miss, storing to remote cache...
4.5.2 强制禁用缓存测试
临时验证构建是否依赖缓存产物:
gradle clean build --no-build-cache
若开启 --build-cache 与禁用的任务输出一致,说明缓存逻辑正确。
4.6 性能与稳定性优化
- 合理设置本地缓存大小:
maxCacheSize = "50 GB"(根据项目产物体积调整),避免频繁清理或磁盘占满。 - 远程缓存超时与重试:Gradle HTTP 缓存客户端默认 30s 超时,可通过系统属性调整:
-Dorg.gradle.cache.http.connectTimeout=10000。 - 缓存键冲突预防:将项目版本、JDK 版本纳入输入参数,避免不同环境使用相同缓存导致异常。可通过
taskOutputs.cacheIf { true }配合自定义 upToDateWhen 控制。 - 并行上传:远程缓存 push 默认单线程,如需更多连接可设置:
remote { isPush = true; maxParallelUploads = 4 }(需自定义缓存后端)。
4.7 常见错误与解决方案
| 错误表现 | 原因 | 解决 |
|---|---|---|
Could not PUT to remote build cache. Received 401 Unauthorized |
凭据缺失或不正确 | 检查 gradle.properties 或环境变量中缓存密码;验证用户名密码是否匹配缓存服务器配置。 |
| 远程缓存始终 miss | URL 错误或缓存协议不匹配 | 确认 URL 是否指向 Gradle 缓存 API 端点(如 /cache/);使用 curl 测试 GET/PUT 能否响应。 |
| 本地缓存项过期太快 | removeUnusedEntriesAfterDays 设得太短或磁盘空间不足 |
增大参数或分配更多磁盘;检查其他进程是否清理了 ~/.gradle/caches/ 目录。 |
| 构建时 timeout 等待远程缓存 | 网络延迟高或远程服务不可用 | 优化连接超时参数;为 remote 缓存块添加 enabled = false 临时关闭,确保本地构建不依赖远程。 |
4.8 内联 SVG 结构图:buildCache 配置作用流程
4.9 生产环境部署检查清单
- 凭据文件隔离:将
gradle.properties中cachePassword写入 CI 机密变量或不纳入版本控制。 - 多环境兼容:开发、CI、预发布环境下分别使用不同的缓存 URL 或禁用远程缓存,避免互相污染。
- 缓存可观测性:集成
org.gradle.caching.debug并在日志中心收集缓存命中率指标。 - 备份与灾难恢复:远程缓存定期备份;若使用 S3/MinIO,配置跨区域复制。
- 持续验证:每次升级 Gradle 版本后,全量构建一次确保缓存键兼容性。
最佳实践:在 settings.gradle 顶部使用 buildCache { local { isEnabled = gradle.startParameter.isBuildCacheEnabled } } 可以让开发者通过 --no-build-cache 自由控制缓存,而远程缓存始终只读。