nginx worker 在缓存未命中时不阻塞,而是异步透传请求至后端或文件系统,响应返回后按规则决定是否缓存;全程基于非阻塞i/o与事件驱动,依赖epoll/kqueue调度,关键配置如proxy_cache_lock、open_file_cache等影响吞吐与延迟。

当 Nginx Worker Process 遇到缓存未命中(cache miss)时,它并不“处理缓存”本身,而是按既定流程将请求继续向下推进——核心动作是:**不缓存、不拦截、不阻塞,快速交由后端或文件系统响应,并在返回后决定是否写入缓存**。整个过程高度依赖事件驱动和非阻塞 I/O,与传统“等待结果再缓存”的同步模型完全不同。
缓存未命中时的典型路径
以反向代理缓存(proxy_cache)为例:
- Worker 接收请求,根据
proxy_cache_key查 keys_zone 元数据区 → 无对应 key 或状态为 STALE/MISS - 立即发起 upstream 连接(复用连接池),把原始请求透传给后端服务
- 同时启动异步等待:不阻塞当前 worker,继续处理其他就绪事件(如其他客户端读/写、定时器)
- 后端响应到达后,worker 检查响应头:
– 若含Cache-Control: no-cache、Set-Cookie(且未配置proxy_ignore_headers)、206或302等,默认跳过缓存写入
– 若符合proxy_cache_valid规则且无禁止头,则触发缓存落盘流程
静态文件未命中的实际行为
对直接服务静态资源(如 location /static/ { root /var/www; })的场景,所谓“缓存未命中”实为文件系统 page cache 未命中:
- Worker 调用
openat()获取文件描述符,再调用sendfile() - 若文件内容不在内核 page cache 中,内核自动触发磁盘读取,worker 不等待——它只是注册一个“文件可读/可发送”事件,继续轮询 epoll
- 数据从磁盘加载进内存后,epoll 通知就绪,worker 才执行
sendfile()将 page cache 数据零拷贝发往 socket - 这个过程没有“缓存写入”,但 page cache 会自然填充,后续相同请求即命中内存
为什么 Worker 不会因此卡住?
关键在于其单线程事件模型的设计哲学:
- 所有 I/O 操作(open/read/sendfile/write/rename)均设为非阻塞模式
- 磁盘读取、网络收发、缓存元数据更新等耗时操作全部异步化,靠 epoll/kqueue 通知驱动
- 即使缓存目录落在慢速 HDD 上,worker 也只是在
rename()(缓存落盘最后一步)时短暂等待;若已配置use_temp_path=off,该等待可大幅缩短 - 真正造成卡顿的是系统级阻塞(如 D 状态进程),这往往源于配置错误(如跨设备
rename、开启directio处理小文件)而非缓存逻辑本身
影响未命中响应效率的关键配置
这些设置不改变“是否缓存”,但决定未命中流量的吞吐与延迟:
-
proxy_buffering on;:启用缓冲,确保完整响应体进入缓存;关闭后可能只缓存部分响应 -
proxy_cache_lock on;:高并发下防穿透,同一 key 的多个未命中请求中仅首个访问后端,其余排队等待——牺牲首字节时间换缓存一致性 -
open_file_cache max=10000 inactive=30s;:缓存文件句柄和元数据,减少stat()和open()系统调用开销,尤其利于大量小静态文件场景 -
sendfile on; tcp_nopush on;:绕过用户态拷贝,降低 CPU 和内存压力,让未命中响应更快发出











