Nginx | HTTP 反向代理:当缓存失效时如何减轻后端(上游)服务压力?

知识是人生的灯塔,只有不断学习,才能照亮前行的道路

📢 大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 🚀,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 😋 。

Nginx 缓存失效时减轻后端(上游)服务压力

上一篇文章《Nginx | HTTP 反向代理:对上游服务端响应缓存流程浅析与配置实践》中,我们学习了 Nginx 反向代理对于上游服务响应的缓存机制,并探究了上游服务器特定响应头对缓存的影响,相信各位看友都已经实践掌握了吧,在此基础下,我们继续探究如何在缓存失效时减轻对上游服务的压力。

在生产环境中,我们常常会遇到这样的场景,在大流量场景下,当 Nginx 缓存因安全服务宕机等原因失效后重新启动,大量用户请求将穿透缓存直接到达上游服务,特别是针对热点资源的并发访问,这会导致上游服务无法承受而持续不可用,为了解决这个问题,我们可以采取一些策略来减轻这种压力,例如:通过合并回源请求、使用过期缓存响应客户端及条件请求头等机制,有效减轻上游服务并发压力的技术方案。

温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_6hpaidhf5l5s.html

方案1:合并回源请求

使用 proxy_cache_lock 等指令(缺省关闭),在缓存失效时合并回源请求,可以有效减轻峰值流量下的压力。当启用此功能后,如果多个客户端同时尝试访问同一资源,并且该资源的缓存已经过期,Nginx 仅允许第一个请求转发至上游服务,其余并发请求需等待首个请求返回或超时后再使用生成的缓存进行响应,并通过proxy_cache_lock_timeoutproxy_cache_lock_age 相关控制指令进行控制首个请求生成缓存的时间,以及首个请求处理时间超过设定阈值,执行流程如下所示:

  • 第一个客户端请求到达 Nginx ,触发向上游发起回源。

  • 第二、三、四等后续请求到来时被阻塞,不立即转发到上游。

  • 首个请求从上游获取响应并建立缓存。

  • 缓存建立后,所有等待中的请求均从缓存中获得响应,从而显著减少对上游的并发冲击。

方案2:使用过期缓存响应客户端

使用 proxy_cache_use_stale 指令,即使缓存已过期,仍可暂时使用旧缓存内容响应客户端,提升用户体验,并避免瞬间大量回源,另外配合 proxy_cache_background_update 指令完成由子请求缓存更新,主请求可立即返回旧缓存内容,进一步提高响应速度,执行流程如下所示:

  • 第一个客户端请求触发回源以更新缓存。

  • 在等待新响应期间,第二个、第三个请求到来时,NGX直接返回旧缓存内容。

  • 上游响应返回后,缓存更新为最新版本。

  • 后续新请求即可获取新缓存内容。

weiyigeek.top-缓存失效后减轻后端压力的两种解决方案图

指令参数

  • proxy_cache_lock: 启用缓存锁,防止多个请求同时更新同一资源,当同一缓存元素的其他请求将等待响应出现在缓存中,或等待缓存锁释放此元素,直到 proxy_cache_lock_timeout 指令设置的时间,主要用于在缓存失效时减轻峰值流量下的压力。
go 复制代码
Syntax: proxy_cache_lock on | off;
Default: proxy_cache_lock off;
Context: http, server, location
  • proxy_cache_lock_age: 指定首个请求处理时间超过设定阈值,简单来说就是第一个请求若在指定时间内未完成,则将第二个请求转发到上游服务器中,以此类推,直至到达 proxy_cache_lock_timeout 指令设置的超时时间。
go 复制代码
Syntax: proxy_cache_lock_age time;
Default: proxy_cache_lock_age 5s;
Context: http, server, location
  • proxy_cache_lock_timeout: 表示其他请求最多等待首个请求生成缓存的时间,默认值为5秒,若超时仍未收到响应,则这些等待请求将同时被放行并发往上游服务。
go 复制代码
Syntax: proxy_cache_lock_timeout time;
Default: 
proxy_cache_lock_timeout 5s;
Context: http, server, location
  • proxy_cache_use_stale: 设置在哪些情况下可以使用陈旧缓存,这对于处理上游服务器故障或网络延迟等情况非常有用。例如,如果设置为 error | timeout,则当请求因错误(如502、504)或超时而失败时,将使用陈旧缓存的响应作为返回,还可与 proxy_cache_lock 指令联合使用,以尽量减少对代理服务器的访问次数。
go 复制代码
Syntax: proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | off ...;
Default: proxy_cache_use_stale off;
Context:http, server, location

# 参数说明:
  error: 请求失败时使用陈旧缓存
  timeout: 请求超时时使用陈旧缓存
  invalid_header: 响应头无效时使用陈旧缓存
  http_xxx: 当上游服务器返回特定HTTP状态码时使用陈旧缓存
  updating: 当缓存项正在更新时使用陈旧缓存,还可与上游特定响应头 Cache-Control 值联合使用,例如:
    # 表示资源在600秒内为新鲜状态,在600至630秒之间为"可接受过期"状态,此时仍可返回过期缓存,同时后台异步更新
    Cache-Control: max-age=600, stale-while-revalidate=30
    # 定义缓存过期后的一段时间内,若上游服务出错(如500、连接超时),则继续使用过期缓存响应客户端,提升系统容错能力,在上游不稳定时维持基本服务能力。
    Cache-Control: max-age=600, stale-if-error=1200
  • proxy_cache_background_update: 允许启动后台子请求以更新过期的缓存项,同时将过时的缓存响应返回给客户端,而无需等待更新完成。这对于提高用户体验和减少延迟非常有用。
go 复制代码
Syntax: proxy_cache_background_update on | off;
Default: proxy_cache_background_update off;
Context: http, server, location
  • proxy_cache_revalidate: 用于减少回源数据传输,启用后(设为on),当缓存过期需回源验证时,Nginx 会自动添加条件请求头 If-None-Match: <ETag>If-Modified-Since: <Last-Modified>,若上游服务判断资源未变化,则返回304 Not Modified,无需传输完整响应体,从而减少带宽消耗。
go 复制代码
Syntax: proxy_cache_revalidate on | off;
Default: proxy_cache_revalidate off;
Context: http, server, location

温馨提示:默认情况下,Nginx 仅对200、206等成功状态码进行缓存,若要缓存错误状态码的响应,可通过proxy_cache_valid 指令显式缓存错误响应,并配合 proxy_cache_background_update,指令可在缓存错误响应的同时发起后台请求尝试恢复正常内容。

go 复制代码
proxy_cache_valid 500 404 1m;

示例演示

步骤 01.使用两台 Nginx 主机作为演示,其中 213 主机作为上游服务分别创建 8010 与 8011 服务端口,214 主机配置反向代理缓存。

go 复制代码
# 在 213 主机上创建一个测试文件,以及上游服务
echo"https://www.weiyigeek.top" >> /usr/local/nginx/html/url.txt

# 创建两个服务端口,分别监听 8010 与 8011
vim /usr/local/nginx/conf.d/server.conf

# 模拟上游服务器其监听端口为8010
server {
  listen 8010;
# 任意主机名
  server_name _;
# 设置字符集为utf-8
  charset utf-8;
# 设置默认响应类型为文本
  default_type text/plain;
# 根目录
  location / {
    root  /usr/local/nginx/html;
    index index.html;
  }
# 模拟api接口响应
  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 < $remote_addr:$remote_port\n';
  }
}

步骤 02.在 214 主机上配置上游服务器组,用于后续反向代理。

go 复制代码
tee backend_server.conf <<'EOF'
# 创建上游服务器
upstream backend_server {
# 定义共享内存区,用于在工作进程间同步负载信息,可根据后端服务器数量调整。
  zone backend_zone 64k;

# 指定多个上游服务组,缺省使用轮询负载均衡算法
  server 10.20.172.213:8010 max_fails=2 fail_timeout=10s max_conns=5;
  server 10.20.172.213:8011 max_fails=2 fail_timeout=10s max_conns=5;

# 设置与上游服务器的长连接,最多保持10个空闲的保活连接。
  keepalive 10;
  keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间。
}
EOF

步骤 03.在 214 主机上配置反向代理缓存,并启用合并回源请求、使用过期缓存响应客户端。

go 复制代码
tee test.conf <<'EOF'
# 定义缓存路径,为 data01 缓存区域分配10MB内存空间,加载缓存索引文件,并设置最大内存使用限制为128MB,不要添加 inactive 参数。
proxy_cache_path /usr/local/nginx/cache levels=2:2 keys_zone=data01:10m loader_threshold=300 loader_files=200 max_size=128m  use_temp_path=off;

# 方案1.合并回源请求
proxy_cache_lock on;
# 设置首个请求处理时间阈值,超过此时间的请求将被转发到上游服务器。
proxy_cache_lock_age 5s;
# 设置其他请求等待首个请求生成缓存的最长时间。
proxy_cache_lock_timeout 5s;


# 虚拟主机配置,反向代理到上游服务器组
server {
  listen 80;
  server_name test.weiyigeek.top;
  charset utf-8;
  default_type text/html;

# 启用 HTTP/2 支持
  http2 on;

# 日志文件
  access_log /var/log/nginx/test.log main;
  error_log /var/log/nginx/test.err.log debug;

  location / {
    proxy_pass http://backend_server;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 启用缓存,指定缓存区域为 data01
    proxy_cache data01;

    # 方案2.当上游异常时以及缓存项正在更新时使用陈旧缓存响应客户端。
    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504 http_429;
    # 在后台异步更新过期的缓存条目,同时返回陈旧数据给客户端。
    proxy_cache_background_update on;
    # 使用陈旧缓存响应客户端,当上游服务出错或延迟较高时。
    proxy_cache_revalidate on;

    # 缓存键,可根据实际需求调整,将默认的 $request_uri 替换为了 $uri 这样可以避免查询参数对缓存键的影响。
    proxy_cache_key $scheme$proxy_host$uri;

    # 响应码为200|404的响应数据进行缓存有效期为5分钟
    proxy_cache_valid 200 404 5m;

    # 自定义响应头,用于验证上游缓存状态
    add_header X-Cache-Status $upstream_cache_status;
  }
}
EOF

步骤 04.重启 Nginx 服务并测试配置文件是否正确无误,使用 curl、xargs 命令模拟并发请求,并观察缓存命中率。

go 复制代码
# 重启 Nginx 服务
nginx -t && nginx -s reload

# 模拟并发请求,例如请求4次,并发为2
seq 1 4 | xargs -I {} -P 2 sh -c 'echo "Rquest id={}"; curl -s -i "http://test.weiyigeek.top/api?id={}";sleep 1'

# 在缓存目录中查看是否生成了对应的文件。
$ tree /usr/local/nginx/cache
/usr/local/nginx/cache
└── 0f
    └── cd
        └── fdf2f00c4f014e3fdca0cdb1f6cacd0f

$ cat /usr/local/nginx/cache/0f/cd/fdf2f00c4f014e3fdca0cdb1f6cacd0f
.....
KEY: httpbackend_server/api
HTTP/1.1 200 OK
Server: openresty/1.27.1.1
Date: Fri, 26 Dec 2025 09:17:08 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 88
Connection: close

2025-12-26T17:20:00+08:00 f1bc005956fe5ea00a4de1ce18eb5af7 10.20.172.213:8011 /api?id=2

weiyigeek.top-模拟并发请求验证命中图

再次,请求上游服务器中 url.txt 文件,例如:seq 1 4 | xargs -I {} -P 2 sh -c 'echo "Rquest id={}"; curl -s -i "http://test.weiyigeek.top/url?id={}"'
weiyigeek.top-模拟并发请求验证上游文件图

最后,访问一个不存在的文件curl -s -i "http://test.weiyigeek.top/notfound.txt,验证我们后端返回的 404 状态码,是否被缓存,以及请求是否被转发到上游。
weiyigeek.top-缓存错误响应码404图

步骤 05.为了验证缓存到期后当上游服务器异常时,仍然返回陈旧缓存响应客户端,此处,我们先将 214 主机的 nginx 停掉, 然后五分钟后再次请求下述 URL,可以发现当客户端请求已过期缓存的资源时,仍然正常的返回陈旧数据。

go 复制代码
curl -s -i "http://test.weiyigeek.top/notfound.txt"
curl -s -i "http://test.weiyigeek.top/api"
curl -s -i "http://test.weiyigeek.top/url.txt"
curl -s -i "http://test.weiyigeek.top/url1.txt"

weiyigeek.top-验证上游异常缓存过期场景图

至此,完成了 Nginx 反向代理缓存的高级配置,包括合并回源请求、使用过期缓存响应客户端等高级特性。通过这些设置,可以有效提升系统的可用性和性能表现。

总结:在高流量生产环境中,合理配置缓存指令可极大提升系统稳定性,可在突发故障或高峰流量下有效保护上游服务,避免雪崩效应。

  • proxy_cache_lock:防止缓存击穿。

  • proxy_cache_use_stale + stale-while-revalidate:保障高可用性。

  • proxy_cache_revalidate:降低回源开销。

  • proxy_cache_background_update:实现无缝更新。

END

加入:作者【全栈工程师修炼指南】知识星球

『 全栈工程师修炼指南』星球,主要涉及全栈工程师(Full Stack Development)实践文章,持续更新包括但不限于企业SecDevOps和网络安全等保合规、安全渗透测试、编程开发、云原生(Cloud Native)、物联网工业控制(IOT)、人工智能Ai,从业书籍笔记,人生职场认识等方面资料或文章。

Q: 加入作者【全栈工程师修炼指南】星球后有啥好处?

✅ 将获得作者最新工作学习实践文章以及网盘资源。

✅ 将获得作者珍藏多年的全栈学习笔记(需连续两年及以上老星球友,也可单次购买)

✅ 将获得作者专门答疑学习交流群,解决在工作学习中的问题。

✅ 将获得作者远程支持(在作者能力范围内且合规)。

获取:作者工作学习全栈笔记

作者整理了10年的工作学习笔记(涉及网络、安全、运维、开发),需要学习实践笔记的看友,可添加作者微信或者回复【工作学习实践笔记】,当前价格¥299,除了获得从业笔记的同时还可进行问题答疑以及每月远程技术支持,希望大家多多支持,收获定大于付出!

知识推荐 往期文章

若文章对你有帮助,请将它转发给更多的看友,若有疑问的小伙伴,可在评论区留言你想法哟 💬!

相关推荐
prettyxian2 小时前
【Linux】内核编织术:task_struct的动态网络
linux·运维·服务器
典孝赢麻崩乐急2 小时前
Redis复习----------Redis超高性能的原因
数据库·redis·学习·缓存
Danileaf_Guo2 小时前
OSPF路由引入的陷阱:为何Ubuntu上静态路由神秘消失?深挖FRR路由分类机制
linux·运维·网络·ubuntu·智能路由器
张某人的胡思乱想2 小时前
windows远程ubuntu
linux·运维·ubuntu
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之mtoolstest命令(实操篇)
linux·运维·前端·笔记·microsoft
iconball2 小时前
个人用云计算学习笔记 --30 华为云存储云服务
运维·笔记·学习·华为云·云计算
catchadmin2 小时前
前后端分离框架 CatchAdmin V5 beta.2 发布 插件化与开发效率的进一步提升
运维·服务器
ocean21032 小时前
Linux面试题图解
linux·运维·服务器·面试·职场和发展
典孝赢麻崩乐急2 小时前
Redis复习-------Redis事务
数据库·redis·缓存