基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

Nginx 的 proxy_cache 机制是如何工作的?

Nginx 的 proxy_cache 机制是其作为反向代理服务器时最强大的功能之一。它允许 Nginx 将后端服务器(Upstream)的响应缓存在本地磁盘上,当后续有相同的请求时,Nginx 可以直接从本地缓存中返回响应,而无需再次请求后端服务器。

这极大地降低了后端服务器的负载,减少了网络延迟,并提高了系统的整体吞吐量。

以下是 proxy_cache 机制工作原理的详细拆解:


1. 核心架构:内存与磁盘的配合

Nginx 的缓存机制依赖于 共享内存磁盘文件系统 的紧密配合,并由两个专门的后台进程来维护:

  • 共享内存 (Shared Memory / keys_zone)
    • 作用:用于存储缓存的 Key(哈希值) 以及缓存的元数据(如文件在磁盘上的路径、大小、过期时间、最后访问时间等)。
    • 优势:Nginx 工作进程(Worker Processes)可以通过查询内存极快地判断某个请求是否命中缓存(Cache Hit)。
  • 磁盘文件 (Disk Storage)
    • 作用:实际的 HTTP 响应头和响应体(数据内容)被持久化存储在磁盘上的特定目录中。
  • Cache Loader 进程
    • 作用:Nginx 启动时运行一次。它会扫描磁盘上的缓存目录,将现有的缓存元数据加载到共享内存中。为了不影响 Nginx 启动速度,它是分批加载的。
  • Cache Manager 进程
    • 作用:周期性地在后台运行。负责清理过期的缓存,以及当缓存总大小超过设定的上限(max_size)时,按照 LRU(最近最少使用) 算法强制淘汰旧缓存。

2. 核心工作流程(生命周期)

当一个客户端请求到达 Nginx 时,proxy_cache 的工作流程如下:

  1. 生成 Cache Key
    Nginx 根据配置的 proxy_cache_key(默认是 $scheme$proxy_host$request_uri,即协议+域名+路径)对当前请求进行拼接,然后计算出 MD5 哈希值
  2. 查询共享内存
    Nginx 在共享内存(keys_zone)中查找这个 MD5 哈希值。
  3. 分支 A:缓存命中 (Cache HIT)
    • 如果在内存中找到了元数据,且判定缓存未过期。
    • Nginx 直接根据元数据中记录的路径,从磁盘读取对应的缓存文件,并返回给客户端。
    • 不与后端服务器发生任何交互。
  4. 分支 B:缓存未命中 (Cache MISS) 或 缓存过期 (EXPIRED)
    • 如果内存中没有记录,或者记录显示已过期。
    • Nginx 将请求转发给后端服务器(Upstream)。
    • 接收到后端响应后,Nginx 会先将响应数据写入一个临时文件(Temp File)。
    • 文件写入完成后,Nginx 将临时文件重命名并移动到最终的缓存目录中,同时更新共享内存中的元数据。
    • 最后,将响应发送给客户端。

3. 关键配置指令解析

要让 proxy_cache 工作,通常需要在 nginx.conf 中进行如下配置:

plaintext
http {
    # 1. 定义缓存路径和规则 (只能在 http 块中)
    proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;

    server {
        listen 80;
        
        location / {
            # 2. 启用特定 zone 的缓存
            proxy_cache my_cache;
            
            # 3. 针对不同状态码设置缓存有效期 (TTL)
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            
            # 4. 反向代理到后端
            proxy_pass http://backend_server;
            
            # 5. 添加 Header 方便调试缓存状态
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

proxy_cache_path 参数详解(非常重要):

  • /data/nginx/cache:磁盘上存储缓存文件的绝对路径。
  • levels=1:2:由于单个目录下文件过多会导致文件系统性能下降,这会将缓存文件分散到两级哈希子目录中。
  • keys_zone=my_cache:10m:在共享内存中开辟 10MB 的空间,命名为 my_cache。(1MB 内存大约可以存储 8000 个缓存 key)。
  • max_size=10g:磁盘缓存空间的上限。超出此大小后,Cache Manager 会启动 LRU 淘汰机制。
  • inactive=60m非活跃淘汰时间。如果一个缓存文件在 60 分钟内没有被访问过,即使它还没到过期时间(TTL),也会被清理掉。
  • use_temp_path=off:建议关闭。让临时文件直接写入缓存目录,避免在临时目录和缓存目录(如果跨文件系统)之间进行不必要的文件拷贝。

4. 高级机制与生产实践

Nginx 的缓存机制还有几个非常强大的高级特性,用于解决高并发场景下的常见问题:

A. 防止缓存击穿 / 惊群效应 (proxy_cache_lock)

当某个热点数据的缓存突然过期,瞬间有 1000 个相同请求到达,如果不加锁,这 1000 个请求会同时穿透到后端服务器,导致后端崩溃。

  • 配置proxy_cache_lock on;
  • 机制:Nginx 只允许第一个请求穿透到后端去拉取数据。其他 999 个请求会被 Nginx 阻塞等待,直到第一个请求拿到数据并放入缓存,随后剩下的请求直接从缓存读取。

B. 后端容灾 / 陈旧数据兜底 (proxy_cache_use_stale)

如果后端服务器宕机、超时或返回 50x 错误,Nginx 可以选择返回已经过期的旧缓存给客户端,而不是向客户端展示错误页面。

  • 配置proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
  • 机制:这极大提升了系统的可用性(HA)。其中的 updating 参数尤为巧妙,它表示当某个缓存过期且有后台请求正在去拉取新数据时,在此期间的其他请求直接返回旧数据,不等待。

C. 条件跳过缓存 (proxy_cache_bypass / proxy_no_cache)

有时候我们不希望使用缓存,比如用户强制刷新(Ctrl+F5)或者用户已登录。

  • 配置proxy_cache_bypass $http_pragma $cookie_sessionid;
  • 机制:如果变量值非空且不为 "0",Nginx 将跳过缓存,直接向后端发起请求获取最新数据(bypass 负责不读旧缓存,no_cache 负责新数据不存入缓存)。

5. 如何查看缓存是否命中?

通过配置 add_header X-Cache-Status $upstream_cache_status;,客户端在浏览器的开发者工具中可以看到该请求的缓存状态:

  • HIT:完美命中缓存。
  • MISS:缓存未命中,请求了后端服务器。
  • EXPIRED:缓存已过期,请求了后端服务器获取新数据。
  • STALE:命中缓存,但缓存已过期。因为配置了 proxy_cache_use_stale 且后端异常或正在更新,所以返回了旧数据。
  • UPDATING:缓存已过期,且正在被另一个请求更新,当前请求直接返回了陈旧数据。
  • BYPASS:因为配置了跳过缓存规则,请求直接穿透到了后端。

总结

Nginx 的 proxy_cache 通过共享内存存索引 + 磁盘存数据的方式实现高效检索。利用 Cache Loader/Manager 进程进行异步管理。结合 proxy_cache_lockproxy_cache_use_stale 等高级指令,它不仅是一个简单的加速工具,更是保护后端服务器、实现高并发和高可用架构的利器。

00:00
00:00