4. 远程缓存配置实战:Remote 类型、HTTP/HTTPS 配置、认证、自定义缓存后端
本地缓存在单机构建中表现良好,但团队协作、CI/CD 场景下需要共享构建产物以消除重复生成。远程缓存(Remote Build Cache)允许构建节点将缓存条目推送到共享存储,其他节点直接下载,实现跨机器缓存命中。
关键指标:正确配置远程缓存后,CI 流水线中全量构建时间可降低 60%–80%,单次增量构建耗时从分钟级降至秒级。
4.1 远程缓存架构
Gradle 远程缓存基于 HTTP/HTTPS 协议(也可扩展为自定义存储后端)。核心交互流程如下:
4.2 远程缓存核心配置
在 settings.gradle 或 init.gradle 中声明远程缓存端点:
// settings.gradle
buildCache {
remote(HttpBuildCache) {
url = 'https://cache.example.com/cache/'
push = true // 允许本地上传缓存条目(CI 节点应开启)
enabled = true
}
}
生产建议:推送权限应只对 CI 或受信节点开放,开发者节点用
push = false 避免污染公共缓存;本地开发机可同时启用本地缓存(默认开启)和只读远程缓存。
4.3 HTTP/HTTPS 认证配置
Gradle 支持三种主流认证方式:
| 认证方式 | 配置方法 | 适用场景 |
|---|---|---|
| Basic Auth(URL 内嵌) | https://user:pass@cache.example.com/cache/ | 简单测试、临时环境(密码明文,不推荐生产) |
| 系统属性 | -Dgradle.buildCache.user=xxx -Dgradle.buildCache.password=xxx | CI 中通过环境变量注入 |
| 自定义 HTTP 头 | 实现 HttpBuildCacheService 接口添加 Authorization 头 | Bearer 或 OAuth2 令牌 |
推荐做法——在 CI 中使用系统属性并配合密钥管理服务:
# 构建脚本中避免硬编码,由构建代理注入
./gradlew build -Dgradle.buildCache.url=https://cache.mycompany.io/cache/ \
-Dgradle.buildCache.user=${CACHE_USER} \
-Dgradle.buildCache.password=${CACHE_PASS}
安全提醒:URL 内嵌凭据会出现在进程列表、日志、构建缓存条目中,严禁用于生产环境。始终使用系统属性或环境变量传递敏感信息。
4.4 缓存命中/未命中分析
启用远程缓存后,通过 --info 日志观察缓存决策:
# 缓存命中日志
> Task :compileJava FROM-CACHE
# 表示远程缓存命中,直接从缓存下载(不重新编译)
# 缓存未命中日志
> Task :compileJava CACHED_INPUTS_CHANGED
# 输入变更触发了重新生成,新结果上传至缓存
使用远程缓存时常见的未命中原因:
- 网络问题:远程端点不可达或超时导致降级为本地构建
- 认证失败:401/403 响应,Gradle 会回退到本地执行
- 不兼容的缓存键:Gradle 版本、Java 版本、构建脚本差异均影响缓存键
- 未开启
push:阅读缓存时找不到可用条目
4.5 自定义缓存后端
当标准 HTTP 缓存无法满足需求(如需对象存储、Redis、数据库),可实现 BuildCacheService 接口:
// 自定义缓存后端示例(伪代码)
import org.gradle.caching.BuildCacheService
import org.gradle.caching.BuildCacheKey
import org.gradle.caching.configuration.BuildCacheConfiguration
class RedisBuildCacheService : BuildCacheService {
private val redis: Jedis
override fun store(key: BuildCacheKey, data: ByteArray) {
redis.setex(key.hashCodeHex, 86400, data) // 24小时过期
}
override fun load(key: BuildCacheKey): ByteArray? {
return redis.get(key.hashCodeHex)?.toByteArray()
}
override fun close() = redis.close()
}
注册自定义服务需在 init.gradle 或插件中配置:
// init.gradle
initscript {
dependencies {
classpath files('redis-cache-plugin.jar')
}
}
settingsEvaluated { settings ->
settings.buildCache {
registerBuildCacheService(RedisBuildCache::class.java, RedisBuildCacheService::class.java)
remote(RedisBuildCache) {
server = 'localhost:6379'
push = true
}
}
}
4.6 性能对比数据
| 场景 | 无缓存 | 仅本地缓存 | 本地 + 远程缓存(命中) |
|---|---|---|---|
| 纯 Java 项目 (500 源文件) | 2 分 10 秒 | 45 秒 | 12 秒 |
| Kotlin + Android (1000 源文件) | 4 分 30 秒 | 1 分 20 秒 | 25 秒 |
| 微服务 CI 流水线(20 module) | 8 分钟 | — | 1 分 15 秒 |
实验步骤:① 定义两个 CI Job——一个全量构建无远程缓存,一个使用已填充的远程缓存;② 记录两个 Job 的
Task :compileJava 耗时;③ 对比数据验证远程缓存效果。推荐先在单机测试:删除本地缓存 ~/.gradle/caches/,仅通过远程缓存构建,确认成功命中。
4.7 常见错误与修复
- 错误:
java.io.IOException: Unexpected HTTP status code 403
修复:检查认证凭据是否正确,确认远程缓存端点允许推送(HTTP PUT)权限。 - 错误:
Build cache returned stale entry
修复:远程缓存后端应强制设置合理的 TTL,或使用基于内容的校验和更新策略。 - 错误:
Could not establish connection to remote cache
修复:验证网络连通性(curl -v https://cache.example.com/cache/),检查 SSL 证书是否受信任。 - 性能退化:远程缓存下载时间比本地编译还长
修复:尽量在区域就近部署缓存节点,或限制单次缓存条目大小(如图 4.1 中可设置最大缓存条目 100MB)。
4.8 调试建议
启用远程缓存调试日志(在 gradle.properties 中):
org.gradle.internal.http.connectionTimeout=30000
org.gradle.internal.http.socketTimeout=30000
systemProp.org.gradle.caching.debug=true
日志会输出每条缓存请求的 URL、耗时、状态码以及是否使用缓存。通过观察 FROM-CACHE 与 CACHED_INPUTS_CHANGED 的比例,可快速判断远程缓存的有效性。