找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1865

积分

0

好友

246

主题
发表于 2025-12-30 20:44:23 | 查看: 22| 回复: 0

在讲解 Nginx 反向代理缓存的具体配置前,理解其内部处理流程至关重要。整个流程可以划分为两个部分:处理客户端请求与处理上游服务器响应。通过流程分析,可以帮助我们更好地理解后续各个缓存指令的具体作用。

第一部分:客户端请求的缓存处理流程

  1. 检查是否开启 proxy_cache 指令。必须配置 proxy_cache zone_name; 指令,其中 zone_name 为共享内存块名称。若未配置,则直接向上游发送请求,不进行任何缓存操作。

  2. 检查请求方法是否匹配 proxy_cache_methods 指令规定的方法,默认只缓存 GET 和 HEAD 请求。

  3. 检查 proxy_cache_convert_head 指令决定是否将 HEAD 请求视为 GET 请求处理。若开启转换,则后续流程按GET请求对待,允许使用缓存。

  4. 使用 proxy_cache_key 指令定义缓存关键字。默认关键字为 $scheme$proxy_host$request_uri 组成,可自定义变量组合生成关键字。关键字经MD5哈希处理以缩短长度并提高查找效率。

  5. 检查 proxy_cache_bypass 指令值。若变量值为真(非空且不为“0“),则跳过缓存,直接向上游发送请求。这常用于动态绕过缓存的场景,如带特定参数时不使用缓存。

  6. proxy_cache 指定的共享内存区域中查找对应关键字的缓存项(使用二叉树查找)。若缓存不存在,则在共享内存中分配新节点,标记该请求将使用缓存并向上游发送请求等待响应。若缓存存在,则更新LRU链表,将当前缓存项移至头部,避免近期被淘汰,同时更新节点访问次数,用于 proxy_cache_min_uses 指令判断。

  7. 检查缓存响应状态与过期情况。若缓存响应为错误类(如404、500)且已过期,则不使用缓存,直接向上游发送请求;若非错误响应且未过期,则直接向下游返回缓存内容;若非错误响应但已过期,便根据 proxy_cache_background_update 指令判断是否启用后台更新。若启用,则立即用过期缓存响应客户端,同时发起子请求更新缓存;若未启用,则阻塞等待上游新响应后再返回。

  8. 共享内存节点分配失败处理。若首次分配节点失败,尝试淘汰已过期的缓存条目释放空间,再次尝试分配;若再次分配仍失败,放弃使用缓存,请求不再经过缓存模块处理,直接透传至上游。

图1:Nginx客户端请求缓存处理流程图
Nginx客户端请求缓存处理流程图

第二部分:接收上游响应的缓存处理流程

  1. 检查 proxy_no_cache 指令。若变量值为真(非空且不为“0“),则不使用缓存。

  2. 检查 proxy_cache_valid 指令。根据响应状态码设置缓存有效期,若响应未匹配此指令规则,则不更新缓存,直接转发响应。

  3. 判断响应码。若响应码为 200(成功)或 206(部分内容),则更新缓存条目的 ETagLast-Modified 字段,以便后续条件请求使用。

  4. 处理影响缓存的响应头部。依次检查可能阻止缓存的头部,如 Set-Cookie 头部、Vary 头部(当未被 proxy_ignore_headers 忽略时)将导致不缓存。可通过 proxy_ignore_headers Set-Cookie; 来忽略 Set-Cookie 头。

  5. 开始读取并转发上游响应。此过程为多次循环操作,因为响应体可能较长,需分块处理。

  6. 当全部响应读取完毕后,将临时文件重命名为正式缓存文件,并移入缓存目录。此操作本质上是 rename 系统调用。若临时目录与缓存目录位于不同磁盘设备上,rename 操作会退化为“拷贝 + 删除”,性能开销大。因此,建议将临时目录(proxy_temp_path)与缓存目录(proxy_cache_path)置于同一设备。

  7. 更新共享内存状态。更新共享内存中维护的缓存索引树(如红黑树),记录新缓存项的键(key)与其对应文件路径的关系,以实现快速查找。

图2:Nginx接收上游响应缓存处理流程图
Nginx接收上游响应缓存处理流程图

了解流程后,我们来看看具体的缓存指令,它们为 Nginx 的缓存系统提供了精细化的控制能力。

核心缓存指令详解

1. 定义缓存载体与启用缓存

  • proxy_cache_path:设置缓存的路径和核心参数,缓存文件将以 MD5 值命名。

    Syntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [min_free=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
    Default: —
    Context: http
    
    # 关键参数说明:
      path: 缓存文件的存储路径
      levels: 指定缓存目录的层级,默认为1:2
      use_temp_path: 是否使用临时路径存储缓存文件,默认为 on。
      keys_zone: 设置共享内存的名称和大小,用于存储缓存元信息。例如:keys_zone=mycache:10m
      inactive: 设置非活跃时间,超过此时间的缓存将被自动清理。例如:inactive=60s
      max_size: 设置最大磁盘空间使用量,超过后由 cache_manager 进程按LRU淘汰。例如:max_size=1g
      manager_files:单次淘汰循环最多处理文件数,默认100
      loader_files:单次加载循环最多处理文件数,默认100
    
    # 示例:
    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
    # 生成的文件路径格式类似:/data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
  • proxy_cache: 指定 proxy_cache_path 指令创建的共享内存区域名称,以启用缓存。
    Syntax: proxy_cache zone | off;
    Default: proxy_cache off;
    Context: http, server, location

2. 定义缓存策略与控制行为

  • proxy_cache_key: 设置缓存的关键字,可使用任意 Nginx 变量组合。

    Syntax: proxy_cache_key string;
    Default: proxy_cache_key $scheme$proxy_host$request_uri;
    Context: http, server, location
    
    # 示例1:包含查询参数
    proxy_cache_key $scheme$proxy_host$uri$is_args$args;
    # 示例2:包含Cookie值
    proxy_cache_key "$host$request_uri$cookie_user";
  • proxy_cache_methods: 允许缓存的请求方法,默认为 GET 和 HEAD。
    Syntax: proxy_cache_methods GET | HEAD | POST ...;
    Default: proxy_cache_methods GET HEAD;
    Context: http, server, location
  • proxy_cache_convert_head: 允许将 HEAD 请求转换为 GET 请求进行缓存。若为 off 则 HEAD 请求不会命中 GET 的缓存。
    Syntax: proxy_cache_convert_head on | off;
    Default: proxy_cache_convert_head on;
    Context: http, server, location
  • proxy_cache_valid: 设置不同响应状态码的缓存时间。

    Syntax: proxy_cache_valid [code ...] time;
    Default: —
    Context: http, server, location
    
    # 示例:
    proxy_cache_valid 5m; # 对200、301、302等响应码缓存5分钟
    proxy_cache_valid 200 302 10m; # 仅对200和302响应码缓存10分钟
  • proxy_cache_min_uses: 设置缓存项在达到此使用次数后才被认为是有效的。
    Syntax: proxy_cache_min_uses number;
    Default: proxy_cache_min_uses 1;
    Context: http, server, location
  • proxy_no_cache: 设置不缓存的请求条件。当任一变量非空(true)时,不缓存该响应。

    Syntax: proxy_no_cache string ...;
    Default: —
    Context: http, server, location
    
    # 示例:
    proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
  • proxy_cache_bypass: 设置不使用缓存的请求条件。即使存在有效缓存,满足条件时也向上游请求。

    Syntax: proxy_cache_bypass string ...;
    Default: —
    Context: http, server, location
    
    # 示例:
    proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;
  • proxy_cache_max_range_offset: 设置缓存范围请求的最大偏移量(字节),超出此值将不使用缓存。
    Syntax: proxy_cache_max_range_offset number;
    Default: —
    Context: http, server, location

缓存状态变量

Nginx 的 $upstream_cache_status 变量记录了缓存命中状态,可在日志或响应头中输出,其值含义如下:

  • HIT:命中缓存
  • MISS: 未命中缓存
  • BYPASS: 因 proxy_cache_bypass 生效而未使用缓存
  • EXPIRED: 缓存已过期(根据上游头判断)
  • STALE: 命中陈旧缓存(因启用陈旧缓存功能)
  • UPDATING: 缓存过期且正在后台更新,当前返回旧内容
  • REVALIDATED: 经异步验证后确认陈旧内容已更新

实践配置:探究上游响应头对缓存的影响

目标:配置 Nginx 反向代理缓存,并测试上游服务器不同响应头对缓存行为的覆盖或影响。

步骤1:配置上游服务器
创建两个监听不同端口(8010, 8011)的服务来模拟上游服务器,并使用 upstream 模块定义服务器组。

tee backend_server.conf << 'EOF'
# 定义上游服务器组
upstream backend_server {
  zone backend_zone 64k;
  server 10.20.172.214:8010;
  server 10.20.172.214:8011;
  keepalive 10;
  keepalive_timeout 60s;
}

# 模拟上游服务器 8010
server {
  listen 8010;
  server_name _;
  charset utf-8;
  default_type text/plain;
  location / {
    root  /usr/local/nginx/html;
    index index.html;
  }
  location ^~ /api {
    return 200 '$time_iso8601 $request_id $server_addr:$server_port $request_uri\n';
  }
}

# 模拟上游服务器 8011
server {
  listen 8011;
  server_name _;
  charset utf-8;
  default_type text/plain;
  location / {
    root  /usr/local/nginx/html;
    index index.html;
  }
  location ^~ /api {
    return 200 '$time_iso8601 $request_id $server_addr:$server_port $request_uri\n';
  }
}
EOF

步骤2:配置Nginx反向代理与缓存
定义缓存路径,并在虚拟主机中启用缓存策略。

tee test.conf << 'EOF'
# 定义缓存路径与共享内存区
proxy_cache_path /usr/local/nginx/cache levels=2:2 keys_zone=data01:10m loader_threshold=300 loader_files=200 max_size=128m inactive=1m;

server {
  listen 80;
  server_name test.weiyigeek.top;
  charset utf-8;
  default_type text/html;
  http2 on;

  location / {
    proxy_pass http://backend_server;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    # 启用缓存
    proxy_cache data01;
    proxy_cache_valid 200 1m; # 缓存200响应1分钟
    add_header X-Cache-Status $upstream_cache_status; # 输出缓存状态
  }
}
EOF

重启Nginx服务后,进行测试。首次请求状态为 MISS,二次请求为 HIT,一分钟后请求为 EXPIRED 并重新获取。

图3:验证基础缓存配置效果
验证基础缓存配置效果

步骤3:测试上游响应头的影响
在上游服务器配置中添加特定响应头,观察其对Nginx缓存行为的覆盖。

  • X-Accel-Expires:将覆盖Nginx中设置的 proxy_cache_valid 时间。
    # 在上游 location / {} 块中添加
    add_header X-Accel-Expires 3;

    测试命令:curl http://test.weiyigeek.top/ads.txt -i
    结果:缓存有效期变为3秒,覆盖了反向代理配置的1分钟。

图4:X-Accel-Expires头覆盖缓存时间
X-Accel-Expires头覆盖缓存时间

  • Cache-Control:同样会覆盖Nginx的缓存设置,并可定义更复杂的缓存行为。
    # 在上游 location ^~ /api {} 块中添加,替换 return 前的注释
    add_header Cache-Control "public, max-age=3, stale-while-revalidate=3";

    测试命令:curl http://test.weiyigeek.top/api -i
    结果:缓存有效期被设置为3秒,并允许在过期后3秒内使用陈旧缓存。

图5:Cache-Control头定义复杂缓存策略
Cache-Control头定义复杂缓存策略

  • *`Vary: ` 头**:此头会导致Nginx无法缓存该响应。
    # 在上游 location / {} 块中注释其他头,添加
    add_header Vary *;

    测试命令:curl http://test.weiyigeek.top -I
    结果:每次请求 X-Cache-Status 均为 MISS,缓存失效。

图6:Vary: 头导致缓存失效
![Vary:
头导致缓存失效](https://static1.yunpan.plus/attachment/c49c7e62947395ae.webp)

通过以上实践,我们深入理解了Nginx反向代理缓存的工作流程、核心指令的用法,以及上游服务器如何通过响应头来影响甚至控制缓存行为。合理配置这些指令和头信息,是构建高效、可控缓存系统的关键。

火箭




上一篇:Helm实战指南:告别K8s YAML配置困境,实现多环境一键部署
下一篇:RabbitMQ高可用实战:HAProxy负载均衡配置与故障转移指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-11 11:55 , Processed in 0.305538 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表