4. 远程缓存配置实战:Remote 类型、HTTP/HTTPS 配置、认证、自定义缓存后端

本地缓存在单机构建中表现良好,但团队协作、CI/CD 场景下需要共享构建产物以消除重复生成。远程缓存(Remote Build Cache)允许构建节点将缓存条目推送到共享存储,其他节点直接下载,实现跨机器缓存命中。

关键指标:正确配置远程缓存后,CI 流水线中全量构建时间可降低 60%–80%,单次增量构建耗时从分钟级降至秒级。

4.1 远程缓存架构

Gradle 远程缓存基于 HTTP/HTTPS 协议(也可扩展为自定义存储后端)。核心交互流程如下:

Gradle 远程缓存架构图 Gradle 远程缓存架构 Gradle 构建进程 任务输出缓存读写 HTTP 缓存服务器 Nexus / Artifactory / S3 自定义存储后端 Redis / 文件系统 / DB 认证 / 授权 Basic Auth / 说明 实线:标准 HTTP/HTTPS 远程缓存;虚线:自定义后端实现 认证层适用于所有远程端点,Gradle 支持在 URL 中内嵌凭据或通过系统属性传递

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=xxxCI 中通过环境变量注入
自定义 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 的比例,可快速判断远程缓存的有效性。