4. 远程缓存服务搭建:HTTP缓存后端、Nginx配置、认证机制、HTTPS支持

Gradle 远程缓存的核心是将构建产物(缓存条目)存储在一个可由多台机器共享的 HTTP 服务后端上。本章以 Nginx 作为 HTTP 缓存后端载体,从零搭建一套支持 认证机制 和 HTTPS 加密 的生产级远程缓存服务。

章节知识图谱 —— 远程缓存服务由 4 个层次组成:
  1. 存储层:文件系统目录,按缓存键(Cache Key)组织目录与文件;
  2. 代理层:Nginx 反向代理,提供 HTTP PUT/GET 接口;
  3. 安全层:Basic Auth 或 认证 + TLS/SSL 传输加密;
  4. 客户端层:Gradle 构建机通过 settings.gradle 配置远程缓存地址。
远程缓存服务架构与请求流程 远程缓存服务架构与请求流程 Gradle Client 🔒 HTTPS 加密层 🔑 认证网关 Nginx Proxy 📦 文件存储 ⚡ 命中/未命中 PUT/GET /cache/ PUT 写入 GET 查询 200 OK (缓存条目) 📌 流程说明 1. Gradle Client 通过 HTTPS 发起 PUT(存储缓存)或 GET(读取缓存)请求。 2. 请求先经 HTTPS 加密传输,到达 Nginx 后由认证网关验证身份。 3. 认证通过后,Nginx 反向代理将请求转发至本地文件存储目录。 4. 读请求时,Nginx 检查文件是否存在;存在则直接返回缓存条目(命中)。 5. 写请求时,Nginx 将请求体写入以缓存键为路径的文件中。 6. 所有通信均强制使用 TLS 1.2+,防止中间人篡改缓存内容。

4.1 远程缓存架构原理

Gradle 远程缓存采用 HTTP REST 风格接口。缓存键(Cache Key)对应 URL 路径,缓存体(序列化的构建产物)对应 HTTP 请求体。Nginx 作为反向代理,将 HTTP 请求映射为本地文件系统读写。

HTTP 方法URL 模式语义Nginx 处理动作
GET/cache/<cache-key>读取缓存条目检查文件是否存在,存在则返回 200 + 文件内容
PUT/cache/<cache-key>存储缓存条目将请求体写入文件,返回 201
HEAD/cache/<cache-key>探测缓存是否存在返回 200 或 404,无响应体
缓存键结构:Gradle 使用 SHA-1 哈希作为缓存键,例如 ab3f8d2c1e...。Nginx 层面可直接将缓存键作为文件名存储,无需二次哈希。

4.2 环境准备与依赖

搭建远程缓存服务需要以下组件:

  • 操作系统:Ubuntu 22.04 LTS / CentOS 7+ 或任何支持 Nginx 的 Linux 发行版;
  • Nginx:版本 ≥ 1.20,需编译 ngx_http_dav_module(默认已包含)以支持 PUT 方法;
  • SSL 证书:自签名证书(测试)或 Let's Encrypt 证书(生产);
  • htpasswd:用于生成 Basic Auth 密码文件(Apache Utils 或 openssl 均可)。
# Ubuntu 安装依赖
sudo apt update
sudo apt install -y nginx apache2-utils openssl

# CentOS / RHEL
sudo yum install -y nginx httpd-tools openssl

4.3 HTTP 缓存后端存储结构

在文件系统上创建一个专用目录作为缓存根目录,Nginx 将以 客户端请求的 URI 路径 为相对路径读写文件。

# 创建缓存存储目录
sudo mkdir -p /var/cache/gradle-remote
sudo chown -R www-data:www-data /var/cache/gradle-remote
sudo chmod 750 /var/cache/gradle-remote

目录结构示例(实际由 Nginx 动态创建):

/var/cache/gradle-remote/
├── a1/
│   └── b2/
│       └── f3d8c2e1...   # 缓存键 = a1b2f3d8c2e1...
├── c4/
│   └── d5/
│       └── e6a7b8c9...   # Nginx 按需创建子目录
└── ...
磁盘空间规划:构建缓存条目体积较大(单个 JAR 可达 50MB+),建议为 /var/cache/gradle-remote 单独挂载磁盘,并设置磁盘配额或定期清理策略。

4.4 Nginx 配置详解

Nginx 配置分为三个关键块:server 块(监听端口)、location 块(路由规则)、dav_methods(启用 PUT/DELETE)。

4.4.1 基础缓存服务器配置(无认证)

# /etc/nginx/sites-available/gradle-cache
server {
    listen 8080;
    server_name cache.example.com;

    # 缓存存储根目录
    root /var/cache/gradle-remote;

    # 启用 HTTP DAV 方法:PUT、DELETE、MKCOL
    dav_methods PUT DELETE MKCOL;

    # 自动创建不存在的目录(按 URI 层级)
    create_full_put_path on;

    # 设置新文件的权限
    dav_access user:rw group:rw all:r;

    location /cache/ {
        # 允许 GET(读取)和 HEAD(探测)
        # PUT/DELETE 由 dav_methods 启用
        limit_except GET HEAD PUT DELETE {
            deny all;
        }

        # 日志格式(记录缓存键和状态)
        access_log /var/log/nginx/gradle-cache-access.log;
        error_log  /var/log/nginx/gradle-cache-error.log;
    }
}
关键指令说明:
  • create_full_put_path on —— 允许 Nginx 按 URL 路径自动创建中间子目录,避免手动建目录;
  • dav_access user:rw group:rw all:r —— 写入的缓存文件权限为 664;
  • limit_except GET HEAD PUT DELETE —— 仅允许缓存操作必需的 HTTP 方法,提升安全性。

4.4.2 启用目录哈希(避免单目录文件过多)

当缓存条目数量极大时,将所有文件放在同一目录会导致文件系统性能下降。建议启用 哈希目录层级 将文件分散存储:

# /etc/nginx/sites-available/gradle-cache (优化版)
server {
    listen 8080;
    server_name cache.example.com;

    root /var/cache/gradle-remote;

    dav_methods PUT DELETE MKCOL;
    create_full_put_path on;
    dav_access user:rw group:rw all:r;

    location /cache/ {
        limit_except GET HEAD PUT DELETE {
            deny all;
        }

        # 使用哈希子目录:按文件名前 2 个字符创建一级子目录
        # 注意:此功能需要 ngx_http_sub_module
        # 实际可通过 Lua 或 rewrite 实现,此处展示标准方案
        access_log /var/log/nginx/gradle-cache-access.log;

        # 若使用 Nginx + Lua 模块,可实现动态子目录
        # 简化方案:Gradle 缓存键本身已足够分散,可直接存储
    }
}
实际建议:Gradle 的缓存键(SHA-1 哈希)本身就是均匀分布的,直接以完整哈希作为文件名即可。若担心单目录文件数超过 10 万,可配合 proxy_store 或自定义 Lua 脚本做两级子目录。

4.5 认证机制实现

远程缓存服务必须限制访问权限,防止缓存数据泄漏或被恶意填充。本章实现 HTTP Basic Authentication 和 Bearer 两种方案。

4.5.1 Basic Auth 配置

使用 htpasswd 创建用户密码文件:

# 创建密码文件(首次创建加 -c)
sudo htpasswd -c /etc/nginx/.htpasswd gradle-cache

# 添加更多用户(不加 -c)
sudo htpasswd /etc/nginx/.htpasswd build-agent-1

在 Nginx location 块中启用认证:

location /cache/ {
    # 启用 Basic Auth
    auth_basic           "Gradle Remote Cache - Authentication Required";
    auth_basic_user_file /etc/nginx/.htpasswd;

    limit_except GET HEAD PUT DELETE {
        deny all;
    }

    dav_methods PUT DELETE MKCOL;
    create_full_put_path on;
    dav_access user:rw group:rw all:r;
    access_log /var/log/nginx/gradle-cache-access.log;
    error_log  /var/log/nginx/gradle-cache-error.log;
}

4.5.2 Bearer 方案(推荐生产使用)

Basic Auth 每次请求都会传输明文密码(虽经 Base64 编码,但非加密)。推荐使用 Bearer 配合 HTTPS 实现更安全的认证:

# /etc/nginx/sites-available/gradle-cache ( 认证版)
location /cache/ {
    # 使用 map 指令从 Authorization 头中提取 
    # 并在 if 中进行校验(示例为静态 )
    set $valid_ "0";

    if ($http_authorization ~ "^Bearer gradle-cache--2024$") {
        set $valid_ "1";
    }

    if ($valid_ = "0") {
        return 401 "Unauthorized - Invalid or missing ";
    }

    limit_except GET HEAD PUT DELETE {
        deny all;
    }

    dav_methods PUT DELETE MKCOL;
    create_full_put_path on;
    dav_access user:rw group:rw all:r;
    access_log /var/log/nginx/gradle-cache-access.log;
    error_log  /var/log/nginx/gradle-cache-error.log;
}
安全提醒:上述 校验方式是示例,生产环境建议使用 Nginx Lua 模块 或 OpenResty 实现动态 验证(如 JWT)。
永远不要将硬编码 提交到版本控制系统中。

4.6 HTTPS 支持配置

HTTPS 为缓存传输提供机密性和完整性保护,是生产环境的强制要求。

4.6.1 生成 SSL 证书(自签名测试用)

# 生成私钥(2048 位 RSA)
sudo openssl genrsa -out /etc/nginx/ssl/gradle-cache.key 2048

# 生成自签名证书(有效期 365 天)
sudo openssl req -new -x509 -days 365 \
    -key /etc/nginx/ssl/gradle-cache.key \
    -out /etc/nginx/ssl/gradle-cache.crt \
    -subj "/CN=cache.example.com"

4.6.2 配置 SSL 终止于 Nginx

# /etc/nginx/sites-available/gradle-cache-ssl
server {
    listen 443 ssl http2;
    server_name cache.example.com;

    # SSL 证书路径
    ssl_certificate     /etc/nginx/ssl/gradle-cache.crt;
    ssl_certificate_key /etc/nginx/ssl/gradle-cache.key;

    # 安全协议与加密套件
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    # 缓存存储配置
    root /var/cache/gradle-remote;
    dav_methods PUT DELETE MKCOL;
    create_full_put_path on;
    dav_access user:rw group:rw all:r;

    location /cache/ {
        auth_basic           "Gradle Remote Cache";
        auth_basic_user_file /etc/nginx/.htpasswd;

        limit_except GET HEAD PUT DELETE {
            deny all;
        }

        access_log /var/log/nginx/gradle-cache-ssl-access.log;
        error_log  /var/log/nginx/gradle-cache-ssl-error.log;
    }
}

# 可选:HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name cache.example.com;
    return 301 https://$server_name$request_uri;
}

4.6.3 使用 Let's Encrypt 免费证书(生产推荐)

# 安装 certbot
sudo apt install -y certbot python3-certbot-nginx

# 获取并自动配置 SSL 证书
sudo certbot --nginx -d cache.example.com

# 证书自动续期(默认已添加 systemd timer)
sudo certbot renew --dry-run

4.7 客户端配置与验证

Gradle 构建端通过 settings.gradle 或 gradle.properties 配置远程缓存服务地址及凭证。

4.7.1 settings.gradle 配置

// settings.gradle
buildCache {
    remote(HttpBuildCache) {
        url = 'https://cache.example.com/cache/'
        credentials {
            username = 'gradle-cache'
            password = 'your-password'
        }
        // 若使用 Bearer :
        // header = ['Authorization': 'Bearer gradle-cache--2024']

        push = true   // 允许本机上传缓存(默认 false)
        enabled = true
    }
}

4.7.2 gradle.properties 配置(通过系统属性注入)

# gradle.properties
org.gradle.caching=true
org.gradle.caching.http.url=https://cache.example.com/cache/
org.gradle.caching.http.username=gradle-cache
org.gradle.caching.http.password=your-password
# org.gradle.caching.http.header=Authorization: Bearer gradle-cache--2024

4.7.3 验证缓存是否生效

# 构建项目(首次构建,缓存未命中)
./gradlew clean build --build-cache

# 再次构建(应命中远程缓存,任务显示 FROM-CACHE)
./gradlew clean build --build-cache

# 查看 Nginx 访问日志确认
tail -f /var/log/nginx/gradle-cache-ssl-access.log

成功命中时,控制台输出示例:

> Task :compileJava FROM-CACHE
> Task :processResources FROM-CACHE
> Task :classes FROM-CACHE
> Task :jar FROM-CACHE

4.8 调试与常见错误

错误现象可能原因排查命令/方法
客户端收到 401 Unauthorized认证凭证错误或缺失检查 credentials {} 或 是否正确;
测试:curl -u user:pass -X GET https://cache.example.com/cache/test.txt
客户端收到 403 ForbiddenIP 白名单限制 / 文件权限不足检查 Nginx allow/deny 指令;
检查 /var/cache/gradle-remote 目录权限
PUT 请求返回 405 Not AllowedNginx 未启用 dav_methods确认配置中包含 dav_methods PUT DELETE MKCOL
HTTPS 连接失败 / SSL 错误证书过期 / 证书与域名不匹配openssl s_client -connect cache.example.com:443 检查证书链
缓存条目未命中但文件存在缓存键大小写 / 路径编码不一致对比客户端日志中的 URL 和服务器文件路径;
确认 Nginx 没有对 URL 做 lowercase 转换
磁盘空间不足缓存文件未清理du -sh /var/cache/gradle-remote;
配置定时清理脚本或设置磁盘配额
调试利器:使用 curl -v 查看完整请求/响应头和状态码,能快速定位认证、TLS 握手或方法权限问题。
# 测试 GET 是否存在
curl -v -u gradle-cache:password \
  https://cache.example.com/cache/ab3f8d2c1e...

# 测试 PUT 上传测试文件
echo "test" | curl -v -X PUT \
  -u gradle-cache:password \
  --data-binary @- \
  https://cache.example.com/cache/test-key.txt

4.9 性能与稳定性优化

优化方向具体措施预期效果
缓存分层Nginx 启用 proxy_cache 将热缓存条目缓存在内存/SSD减少磁盘 I/O,提升热门缓存读取速度 3~5x
连接复用客户端启用 HTTP Keep-Alive(Gradle 默认)减少 TCP 握手开销,尤其适合大量小缓存条目的读写
限速与并发控制使用 limit_rate 限制单个客户端带宽;
limit_conn 限制并发连接数
防止单客户端耗尽服务器资源
磁盘 IO 优化缓存目录挂载到 SSD 或 tmpfs(小缓存)降低写入延迟,提升缓存存储速度
监控与告警暴露 Nginx stub_status 指标;
集成 Prometheus + Grafana
实时掌握缓存命中率、QPS、磁盘使用量
自动清理使用 tmpreaper 或自建脚本删除超过 N 天未访问的文件避免磁盘写满导致服务不可用
生产级 Nginx 配置建议(综合优化):
  • HTTPS 会话缓存:ssl_session_cache shared:SSL:10m; 提升 TLS 握手性能;
  • Gzip 压缩:对缓存响应启用 gzip on(需注意缓存条目本身可能已是压缩格式);
  • 日志轮转:配置 logrotate 按天切割访问日志,避免磁盘被日志填满;
  • 使用 sendfile 和 tcp_nopush 提升静态文件传输效率。

4.10 部署检查清单

将远程缓存服务投入生产前,逐项确认以下要点:

  • ✅ Nginx dav_methods 已正确启用,PUT/GET 返回预期状态码;
  • ✅ 认证机制生效,未授权请求返回 401;
  • ✅ HTTPS 证书有效,客户端无 TLS 警告;
  • ✅ 缓存目录权限为 www-data:www-data,Nginx 工作进程可读写;
  • ✅ 防火墙仅开放 443 端口(或 8080 用于内部测试);
  • ✅ 已配置磁盘空间监控和自动清理策略;
  • ✅ 在 Gradle 客户端执行 --build-cache 构建验证完整流程:PUT → 201 → GET → 200 → FROM-CACHE。
安全底线:永远不要在未启用认证和 HTTPS 的情况下将远程缓存服务暴露在公网。Gradle 缓存条目包含完整的构建产物(字节码、资源文件),未授权访问可能导致敏感信息泄漏。