05-缓存、静态资源与性能优化
05-缓存、静态资源与性能优化
1. 阶段定位
第五阶段是 Nginx 从“安全可靠入口层”进入“高性能资源治理层”的关键阶段。前四个阶段已经解决了部署、配置体系、请求匹配、反向代理、负载均衡、HTTPS、安全治理和入口访问控制。本阶段重点解决下面这些问题:
1 | 一个请求进入 Nginx 后: |
Nginx 的性能优化不是把所有参数调大,也不是盲目开启缓存。真正重要的是先理解资源类型、请求链路和瓶颈位置,再选择合适的优化手段。
完成本阶段后,应该具备下面的能力:
- 能设计静态资源目录结构、缓存头、过期时间、版本化文件和回滚策略。
- 能解释浏览器强缓存、协商缓存、代理缓存、CDN 缓存和 Nginx 本地缓存的区别。
- 能正确配置
expires、Cache-Control、ETag、Last-Modified等缓存相关响应头。 - 能配置 gzip 压缩,并理解压缩等级、压缩类型、CPU 消耗和传输收益。
- 能理解 Brotli 的适用场景,以及开源 Nginx 默认不内置 Brotli 的事实。
- 能配置
sendfile、tcp_nopush、tcp_nodelay、aio、directio等静态文件发送相关参数。 - 能使用
open_file_cache减少频繁文件元数据查询带来的系统调用开销。 - 能设计和落地
proxy_cache,包括缓存区、缓存 key、TTL、状态码策略、绕过规则、锁、防穿透和清理。 - 能识别哪些内容不能缓存,避免缓存用户隐私数据、权限数据、个性化响应和错误响应。
- 能使用
wrk、ab、hey、curl、日志字段和系统指标评估优化效果。 - 能从 CPU、内存、磁盘 IO、网络带宽、文件描述符、连接状态和 upstream 能力判断瓶颈。
2. 学习边界
2.1 本阶段重点
本阶段重点关注 Nginx 性能优化和缓存治理,包括:
- 静态资源服务优化。
- 浏览器缓存与 HTTP 缓存语义。
expires与Cache-Control。ETag与Last-Modified。- 前端构建产物缓存策略。
- 图片、CSS、JS、字体、下载文件缓存策略。
- gzip 压缩配置与验证。
- Brotli 基础认知与部署边界。
sendfile、tcp_nopush、tcp_nodelay。aio、directio与大文件下载。open_file_cache文件缓存。proxy_cache_path、proxy_cache、proxy_cache_key。- 缓存 TTL、状态码缓存、缓存锁、缓存绕过、缓存刷新。
X-Cache-Status缓存命中观测。- 防止缓存穿透、缓存击穿和缓存雪崩。
- Nginx worker、连接数和文件描述符调优。
- Linux 内核参数与网络队列基础调优。
- 压测方法、指标解释和性能优化闭环。
2.2 本阶段暂不深入
下面内容与性能有关,但不是本阶段重点:
- CDN 厂商平台的完整边缘缓存体系。
- OpenResty Lua 动态缓存和本地字典缓存。
- Redis、Memcached 等应用层缓存设计。
- Kubernetes Ingress Controller 性能注解体系。
- HTTP/3、QUIC 的深入机制。
- Nginx 源码中的 sendfile、event loop 和 filter chain 实现细节。
- 大规模 WAF、Bot 管理、边缘计算和全局流量调度。
- 自研缓存淘汰算法或代理缓存模块开发。
本阶段目标不是把 Nginx 变成完整 CDN,而是掌握生产中最常见、最实用的静态资源优化、代理缓存和系统性能调优能力。
3. 性能优化的基本原则
3.1 不要先调参数,要先定位瓶颈
很多人学习 Nginx 性能优化时,会先搜索一份“高并发配置模板”,然后直接复制下面这些配置:
1 | worker_processes auto; |
这些配置本身并不一定错,但如果不了解瓶颈位置,盲目使用可能没有收益,甚至产生副作用。
常见瓶颈来源包括:
| 瓶颈位置 | 表现 | 常见原因 |
|---|---|---|
| CPU | top 显示 Nginx worker CPU 很高 |
gzip 等压缩开销、TLS 开销、日志过多、正则匹配复杂 |
| 内存 | 系统内存持续上涨或发生 OOM | 缓冲区过大、连接过多、缓存区规划不合理 |
| 磁盘 IO | 静态文件或缓存读取慢 | 磁盘性能差、缓存目录随机 IO 多、日志同步写入压力大 |
| 网络带宽 | Nginx CPU 不高但吞吐上不去 | 出口带宽打满、网卡队列瓶颈、包量过大 |
| 文件描述符 | 出现 too many open files |
worker_rlimit_nofile、系统 ulimit 太低 |
| 连接队列 | 大量连接超时或拒绝 | somaxconn、backlog、worker 连接数不足 |
| 上游服务 | Nginx 等待 upstream 时间长 | 后端慢、连接池不足、数据库慢、线程池耗尽 |
| 客户端 | 日志中 $request_time 高但 upstream 不高 |
客户端网络慢、下载慢、移动网络不稳定 |
性能优化要先回答:
1 | 当前慢在哪里? |
3.2 性能优化闭环
推荐使用下面的闭环做优化:
1 | 明确目标 |
不要一次改十几个参数。否则性能变好或变差时,很难知道真正原因。
3.3 常见优化目标
不同场景下,优化目标不同:
| 场景 | 优化目标 | 重点手段 |
|---|---|---|
| 静态站点 | 降低延迟、提高吞吐、减少带宽 | sendfile、缓存头、gzip、Brotli、文件版本化 |
| 图片站点 | 提高命中率、降低源站压力 | 长缓存、CDN、文件名版本化、合理压缩 |
| API 网关 | 降低后端压力、控制突发流量 | proxy_cache、限流、连接复用、超时治理 |
| 下载站点 | 稳定大文件传输 | sendfile、aio、directio、限速、Range 支持 |
| 高并发入口 | 增加连接承载能力 | worker、连接数、文件描述符、内核队列 |
| 低带宽环境 | 减少传输体积 | gzip、Brotli、图片压缩、缓存 |
| 后端慢接口 | 减少重复请求 | 短 TTL 代理缓存、缓存锁、降级策略 |
4. 静态资源服务基础
Nginx 天然适合服务静态资源。静态资源通常包括:HTML 页面、CSS 文件、JavaScript 文件、图片文件、字体文件、视频文件、下载文件、前端构建产物、文档、压缩包和安装包。
最基础的静态资源配置如下:
1 | server { |
假设请求 GET http://static.example.com/assets/app.js,实际查找路径是 /data/www/static/assets/app.js。
4.1 root 与 alias 复习
root 是把请求 URI 追加到指定目录后面:
1 | location /images/ { |
请求 /images/a.png 对应文件路径 /data/www/images/a.png。
alias 是把 location 前缀替换成指定目录:
1 | location /images/ { |
请求 /images/a.png 对应文件路径 /data/pictures/a.png。
生产中要特别注意:alias 目录通常要以 / 结尾,location 前缀也建议以 / 结尾,避免路径拼接异常。
4.2 静态资源目录建议
推荐将不同类型资源拆分目录:
1 | /data/www/site/ |
不同目录使用不同缓存策略:
| 目录 | 资源特点 | 建议缓存策略 |
|---|---|---|
/assets/ |
构建产物,文件名带 hash | 长缓存,一年或更久 |
/img/ |
图片资源,较少变化 | 较长缓存,按版本或路径更新 |
/fonts/ |
字体文件,较少变化 | 长缓存,注意跨域 |
/upload/ |
用户上传内容,可能变化 | 按业务决定,谨慎长缓存 |
/download/ |
大文件下载 | 支持 Range,可限速 |
/index.html |
前端入口 HTML | 不建议长缓存,通常短缓存或不缓存 |
/api/ |
动态接口 | 默认不按静态资源缓存 |
4.3 前端单页应用配置
前端 SPA 常见配置:
1 | server { |
这里的关键点是:
/assets/中的文件名通常带 hash,例如app.8f3a2c.js,可以长缓存。index.html是入口文件,经常引用新的资源文件,不应该长缓存。try_files $uri $uri/ /index.html用于支持前端路由刷新。- API 请求不要误落入
/index.html,一般应单独配置/api/代理。
4.4 错误示例:把所有内容都长缓存
错误配置:
1 | location / { |
问题:
index.html被长缓存后,前端发布新版本,用户仍可能加载旧入口文件。- 如果 HTML 引用的新旧资源不一致,可能出现白屏、接口异常或静态文件 404。
- 动态页面或用户相关页面被缓存,可能产生严重安全问题。
正确思路:入口 HTML 短缓存或不缓存,带 hash 的静态资源长缓存,动态接口默认不缓存,用户相关内容谨慎缓存。
5. HTTP 缓存基础
HTTP 缓存主要分为三类:
1 | 强缓存 |
5.1 强缓存
强缓存主要由下面响应头控制:
1 | Cache-Control: max-age=31536000 |
现代浏览器优先使用 Cache-Control。Expires 是 HTTP/1.0 时代的字段,仍常用于兼容。
Nginx 配置示例:
1 | location /assets/ { |
如果同时使用 expires 和 add_header Cache-Control ...,需要避免多个 Cache-Control 头语义冲突。生产中建议统一规划,明确每类资源的缓存策略。
5.2 Cache-Control 常用指令
| 指令 | 含义 | 常见场景 |
|---|---|---|
public |
可以被浏览器和共享缓存保存 | 静态资源 |
private |
只能被用户浏览器缓存,不应被共享缓存保存 | 用户个人页面 |
max-age=秒 |
浏览器缓存有效期 | 静态资源、短缓存接口 |
s-maxage=秒 |
共享缓存有效期,优先于 max-age |
CDN、代理缓存 |
no-cache |
可以存储,但每次使用前必须验证 | HTML、频繁变化资源 |
no-store |
不允许存储 | 登录、支付、敏感数据 |
must-revalidate |
过期后必须重新验证 | 严格一致性资源 |
immutable |
有效期内资源不会变化 | 带 hash 的构建产物 |
常见策略:
1 | # 带 hash 的静态资源 |
5.3 协商缓存
协商缓存主要依赖 Last-Modified、If-Modified-Since、ETag、If-None-Match。
流程:
1 | 第一次请求: |
Nginx 默认会对静态文件返回 Last-Modified,也通常会启用 ETag。可以通过下面指令控制:
1 | etag on; |
不建议随意关闭 ETag。如果有多台静态资源服务器,且文件元数据不一致,可能出现 ETag 不一致问题,此时可以考虑关闭 ETag,然后依赖文件名 hash 和 Cache-Control 做版本治理。
5.4 304 不是错误
访问日志中如果看到大量 304,一般不是错误。它表示客户端缓存可用,服务端无需返回完整响应体。
| 状态码 | 含义 | 是否异常 |
|---|---|---|
200 |
返回完整资源 | 正常 |
304 |
资源未变化,使用本地缓存 | 正常 |
404 |
文件不存在 | 需要检查路径或发布结果 |
403 |
权限不足或目录禁止访问 | 需要检查权限和配置 |
416 |
Range 请求范围不合法 | 大文件下载场景需要关注 |
6. 静态资源缓存策略设计
6.1 推荐策略总表
| 资源类型 | 是否适合长缓存 | 推荐策略 |
|---|---|---|
| HTML 入口文件 | 否 | no-cache 或短缓存 |
| 带 hash 的 JS/CSS | 是 | public, max-age=31536000, immutable |
| 不带 hash 的 JS/CSS | 谨慎 | 短缓存或改造成带版本号 |
| 图片资源 | 通常适合 | 长缓存,依赖 URL 版本变化 |
| 字体资源 | 适合 | 长缓存,注意 CORS |
| 用户头像 | 视业务而定 | URL 带版本号可长缓存,否则短缓存 |
| 下载文件 | 适合 | 长缓存,支持 Range |
| API 响应 | 默认不适合 | 根据接口语义单独设计 |
| 登录态页面 | 不适合 | private, no-store |
| 支付、订单、账户 | 不适合 | no-store |
6.2 文件名 hash 是长缓存前提
长缓存最可靠的方式是“内容变,文件名变”。例如:
1 | app.js 不适合长缓存 |
推荐发布流程:
1 | 构建生成带 hash 文件 |
不要发布新版本时立即删除旧资源。因为用户浏览器可能还缓存着旧的 index.html,它仍会引用旧 hash 文件。如果旧文件被删除,就会出现静态资源 404。
6.3 SPA 发布缓存策略
推荐配置:
1 | server { |
注意顺序:/api/ 要优先独立代理,不要落到前端路由;/assets/ 带 hash 的资源可以长缓存;index.html 不要长缓存;前端路由由最后的 location / 兜底。
6.4 字体跨域缓存
如果字体资源被多个域名引用,可能需要 CORS:
1 | location ~* \.(?:woff|woff2|ttf|otf)$ { |
不要对所有资源无脑加 Access-Control-Allow-Origin "*"。字体、公开图片等静态资源可以这样做,但带用户身份或敏感数据的接口不能这样处理。
7. gzip 压缩
7.1 gzip 解决什么问题
gzip 用 CPU 换带宽。它适合压缩文本类资源,例如 HTML、CSS、JavaScript、JSON、XML、SVG 等。它不适合重复压缩已经压缩过的资源,例如 jpg、png、webp、gif、mp4、zip、gz、br 等。
压缩收益取决于资源类型:
| 类型 | gzip 收益 | 说明 |
|---|---|---|
| HTML | 高 | 文本重复多 |
| CSS | 高 | 文本规则多 |
| JS | 高 | 文本重复多 |
| JSON | 高 | 字段名重复多 |
| SVG | 高 | XML 文本 |
| JPEG/PNG/WebP | 低 | 已经是压缩格式 |
| ZIP/GZ/BR | 几乎无收益 | 已压缩 |
| 视频 | 几乎无收益 | 已压缩 |
7.2 gzip 基础配置
推荐放在 http 上下文:
1 | http { |
参数说明:
| 指令 | 说明 | 建议 |
|---|---|---|
gzip on |
开启 gzip | 常用 |
gzip_comp_level |
压缩等级,1-9 | 生产常用 4-6 |
gzip_min_length |
小于该大小不压缩 | 1KB 或 2KB 起 |
gzip_vary |
返回 Vary: Accept-Encoding |
建议开启 |
gzip_proxied |
对代理请求启用压缩 | 视场景配置 |
gzip_types |
指定压缩 MIME 类型 | 只压缩文本类 |
gzip_disable |
禁用老旧客户端 | 一般保留 |
7.3 gzip 验证
使用 curl 验证:
1 | curl -I -H 'Accept-Encoding: gzip' http://static.example.com/assets/app.js |
重点看:
1 | Content-Encoding: gzip |
也可以下载对比大小:
1 | curl -H 'Accept-Encoding: gzip' -o /tmp/app.js.gz http://static.example.com/assets/app.js |
7.4 gzip 常见问题
开启 gzip 后没生效,优先检查客户端是否发送 Accept-Encoding: gzip、响应 Content-Type 是否在 gzip_types 中、文件大小是否小于 gzip_min_length、是否已经被上游压缩、是否命中了不压缩的 location、是否为图片或视频等不适合 gzip 的类型。
压缩等级不是越高越好。gzip_comp_level 9 会显著增加 CPU 消耗,但比 5 或 6 多出来的压缩收益通常有限。CPU 充足且带宽昂贵时可以提高到 6;高并发且 CPU 紧张时使用 4 或 5;极端高吞吐静态站应考虑预压缩或 CDN。
8. Brotli 压缩
8.1 Brotli 是什么
Brotli 是一种压缩算法,通常对文本资源的压缩率优于 gzip,尤其适合 HTML、CSS、JavaScript、JSON、SVG 等资源。浏览器通过 Accept-Encoding: br, gzip, deflate 声明支持,服务端通过 Content-Encoding: br 返回压缩结果。
8.2 Nginx 与 Brotli 的关系
开源 Nginx 官方默认模块中不包含 Brotli。要使用 Brotli,常见方式是:
- 使用带 Brotli 模块的发行版。
- 自行编译第三方
ngx_brotli模块。 - 在 CDN 层开启 Brotli。
- 对静态资源做预压缩,配合支持模块返回
.br文件。
如果没有明确的编译和维护能力,不建议在生产核心入口上随意引入第三方模块。很多场景下,在 CDN 层开启 Brotli 更简单、更稳定。
8.3 Brotli 配置示例
如果已经安装 Brotli 模块,常见配置类似:
1 | brotli on; |
验证:
1 | curl -I -H 'Accept-Encoding: br' http://static.example.com/assets/app.js |
重点看 Content-Encoding: br。
8.4 gzip 与 Brotli 如何选择
| 场景 | 建议 |
|---|---|
| 普通站点 | gzip 足够 |
| 前端静态资源较多 | gzip 必开,可考虑 CDN Brotli |
| 带宽成本很高 | Brotli 有价值 |
| CPU 已经紧张 | 谨慎动态 Brotli |
| 没有模块维护能力 | 不建议自行编译第三方模块 |
| 已使用 CDN | 优先在 CDN 层开启 Brotli |
9. sendfile 与文件发送优化
9.1 sendfile 的作用
传统文件发送大致流程:
1 | 磁盘文件 |
开启 sendfile 后,可以减少用户态和内核态之间的数据拷贝:
1 | 磁盘文件 |
配置:
1 | http { |
对于静态文件服务,通常建议开启。它尤其适合 CSS、JS、图片、下载文件等直接从磁盘读取并返回给客户端的场景。
9.2 tcp_nopush
tcp_nopush 通常与 sendfile 一起使用,让 Nginx 尽量把响应头和文件内容组织成更合适的数据包发送。
1 | http { |
适合场景:
- 静态文件服务。
- 大响应体返回。
- 下载文件。
- 希望减少网络包数量的场景。
9.3 tcp_nodelay
tcp_nodelay 用于禁用 Nagle 算法,减少小包延迟,常用于 keepalive 连接:
1 | http { |
生产中常见组合:
1 | sendfile on; |
不要把这三个参数理解成互相替代。它们作用在不同阶段和不同发送行为上:sendfile 关注文件发送路径,tcp_nopush 关注尽量合并包,tcp_nodelay 关注降低小响应延迟。
9.4 大文件下载配置
大文件下载示例:
1 | location /download/ { |
说明:
Accept-Ranges bytes表示支持断点续传。limit_rate_after 10m表示前 10MB 不限速。limit_rate 2m表示之后限制每个连接约 2MB/s。- 限速可以避免少量大下载连接长期占满出口带宽。
10. aio、directio 与大文件场景
10.1 aio 的定位
aio 用于异步文件 IO,在部分大文件或特殊文件系统场景中有价值。但对于多数普通静态资源站点,开启 sendfile 已经足够。
Linux 下常见配置:
1 | location /video/ { |
这类配置要结合 Nginx 编译参数、内核版本、文件系统和实际压测结果决定,不建议照抄。
10.2 directio 的作用
directio 可以让大文件读取绕过系统页缓存,避免大文件下载把页缓存冲掉,影响小文件访问。
示例:
1 | directio 8m; |
含义:大于等于 8MB 的文件使用 directio。
适合:
- 视频文件。
- 大安装包。
- 大压缩包。
- 大下载站点。
不适合:
- 小文件很多的静态站点。
- 前端 CSS/JS/图片资源。
- 没有压测验证的普通业务入口。
11. open_file_cache 文件缓存
11.1 open_file_cache 解决什么问题
Nginx 服务静态文件时,需要频繁检查文件是否存在、文件元数据、权限等信息。这会产生系统调用,例如 stat()、open()。
open_file_cache 可以缓存:
- 打开的文件描述符。
- 文件大小和修改时间。
- 目录是否存在。
- 文件查找错误,例如 404。
这可以减少高频访问静态资源时的系统调用开销。
11.2 配置示例
1 | http { |
参数说明:
| 指令 | 说明 |
|---|---|
open_file_cache max=10000 inactive=60s |
最多缓存 10000 个条目,60 秒未访问则移除 |
open_file_cache_valid 120s |
每 120 秒检查一次缓存条目有效性 |
open_file_cache_min_uses 2 |
inactive 时间内访问至少 2 次才缓存 |
open_file_cache_errors on |
缓存文件不存在、权限错误等结果 |
11.3 注意事项
open_file_cache_errors on 会缓存 404 等错误结果。如果刚发布了新文件,但之前请求过并缓存了不存在状态,短时间内可能仍返回 404,直到缓存有效性检查更新。
如果发布频繁,配置不要过于激进:
1 | open_file_cache max=5000 inactive=30s; |
12. 代理缓存 proxy_cache
12.1 proxy_cache 是什么
proxy_cache 是 Nginx 作为反向代理时,把上游响应缓存到本地磁盘。后续相同请求可以直接由 Nginx 返回,减少 upstream 压力。
请求链路:
1 | 第一次请求: |
适合缓存:公开页面、新闻文章页、商品详情页的非登录态版本、配置类接口、热点榜单、查询成本高但短时间可接受旧数据的接口、图片缩略图处理结果。
不适合缓存:登录后的用户首页、购物车、订单、支付、用户资料、权限相关接口、带 Cookie 的个性化响应、返回私密数据的接口。
12.2 proxy_cache_path
代理缓存首先要定义缓存区:
1 | http { |
参数说明:
| 参数 | 含义 |
|---|---|
/data/nginx/cache/api |
缓存文件存储目录 |
levels=1:2 |
缓存文件目录层级,避免单目录文件过多 |
keys_zone=api_cache:100m |
共享内存区名称和大小,用于存储缓存 key 元数据 |
max_size=10g |
缓存目录最大体积 |
inactive=60m |
60 分钟未被访问的缓存可被清理 |
use_temp_path=off |
临时文件直接放缓存目录,减少跨文件系统 rename 成本 |
缓存目录要确保 Nginx worker 用户有读写权限:
1 | mkdir -p /data/nginx/cache/api |
12.3 基础 proxy_cache 配置
1 | upstream backend_api { |
$upstream_cache_status 常见值:
| 值 | 含义 |
|---|---|
MISS |
未命中缓存,请求了上游 |
HIT |
命中缓存,直接返回 |
EXPIRED |
缓存过期,请求上游刷新 |
STALE |
使用了过期缓存 |
UPDATING |
正在更新缓存,其他请求使用旧缓存 |
BYPASS |
绕过缓存 |
REVALIDATED |
重新验证后继续使用缓存 |
12.4 缓存 key 设计
缓存 key 决定“什么样的请求算同一个缓存”。常见 key:
1 | proxy_cache_key "$scheme$request_method$host$request_uri"; |
包括协议、请求方法、Host、路径和查询参数。如果接口响应与语言有关,需要加入语言:
1 | proxy_cache_key "$scheme$request_method$host$request_uri$http_accept_language"; |
错误示例:
1 | proxy_cache_key "$uri"; |
问题:忽略查询参数,/search?q=a 和 /search?q=b 可能命中同一份缓存;忽略 Host,不同域名可能串缓存;忽略协议和方法,缓存边界不清晰。
12.5 缓存方法
默认只缓存 GET 和 HEAD。通常不要缓存 POST。
如果确实要缓存某些 POST 查询接口,必须非常谨慎:
1 | proxy_cache_methods GET HEAD POST; |
但这会带来风险:请求体过大导致 key 设计困难,请求体包含敏感数据,请求体顺序不同但语义相同,Nginx 变量读取请求体也存在额外条件。生产中更推荐把可缓存查询设计成 GET 接口,并把参数体现在 URL 中。
12.6 缓存 TTL 设计
不同接口 TTL 应根据业务接受的数据新鲜度决定:
| 内容 | 建议 TTL |
|---|---|
| 新闻详情页 | 1-10 分钟 |
| 商品详情基础信息 | 10 秒-5 分钟 |
| 商品价格库存 | 谨慎,通常短 TTL 或不缓存 |
| 首页推荐 | 10 秒-1 分钟 |
| 热点榜单 | 10 秒-5 分钟 |
| 配置字典 | 5-60 分钟 |
| 公开图片处理结果 | 数小时到数天 |
| 404 结果 | 10 秒-1 分钟 |
| 5xx 错误 | 通常不缓存 |
示例:
1 | location /news/ { |
12.7 绕过缓存
常见绕过条件:请求带登录 Cookie、请求带 Authorization、请求参数包含刷新标记、后台预览请求、非 GET/HEAD 请求。
可以使用 map 定义变量:
1 | http { |
location 中使用:
1 | location /public/ { |
区别:
| 指令 | 作用 |
|---|---|
proxy_cache_bypass |
本次请求不读缓存,直接请求上游 |
proxy_no_cache |
本次响应不写入缓存 |
通常二者会配合使用。
12.8 不缓存 Set-Cookie 响应
上游如果返回 Set-Cookie,往往意味着响应与用户状态有关。一般不应该缓存:
1 | proxy_no_cache $upstream_http_set_cookie; |
也可以忽略某些上游头,但要谨慎:
1 | proxy_ignore_headers Cache-Control Expires Set-Cookie; |
这会无视上游的缓存控制,风险很高。只有在你非常确定响应是公共内容,并且上游缓存头不可信时才考虑使用。
12.9 缓存锁 proxy_cache_lock
热点缓存过期时,如果大量请求同时进入,可能同时打到 upstream,形成缓存击穿。
1 | proxy_cache_lock on; |
含义:第一个请求负责回源刷新缓存,其他相同 key 的请求等待或使用旧缓存,从而减少同一热点资源过期瞬间对后端的冲击。
12.10 使用过期缓存
当 upstream 出错时,可以返回过期缓存提升可用性:
1 | proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; |
适合新闻详情、商品基础信息、配置类接口、首页公共模块。不适合价格强一致、库存强一致、权限判断、支付订单等场景。
12.11 完整代理缓存示例
1 | proxy_cache_path /data/nginx/cache/public |
13. 缓存清理与刷新
13.1 为什么需要缓存清理
缓存带来性能收益,也带来一致性问题。常见需求包括内容发布后立即刷新、商品下架后立即清理详情页、错误内容被缓存后立即删除、用户投诉看到旧内容、运营后台更新后需要生效。
13.2 开源 Nginx 的清理限制
开源 Nginx 默认没有内置按 URL 主动清理 proxy_cache 的官方指令。常见方案:
| 方案 | 说明 |
|---|---|
| 短 TTL | 最简单,依赖自然过期 |
| 版本化 URL | 内容变化时 URL 变化 |
| 删除缓存目录 | 粗暴,只适合低风险场景 |
| 第三方 purge 模块 | 需要额外编译维护 |
| Nginx Plus API | 商业版本能力 |
| CDN 刷新接口 | 如果使用 CDN,优先使用 CDN purge |
13.3 推荐策略
多数业务推荐组合:
1 | 静态资源:文件名 hash + 长缓存 |
不要依赖“手工删除 Nginx 缓存文件”作为常规发布流程。缓存文件路径是根据 key hash 后生成的,手工定位和删除容易出错。
14. 防缓存穿透、击穿、雪崩
14.1 缓存穿透
缓存穿透是指大量请求查询不存在的数据,每次都无法命中缓存,持续打到 upstream。
1 | /article/999999999 不存在 |
治理方法:对 404 设置很短 TTL,在应用层校验 ID 格式,对异常请求限流,使用 WAF 或网关规则拦截明显扫描。
1 | proxy_cache_valid 404 30s; |
注意:404 缓存时间不能太长,否则新内容发布后可能仍返回旧 404。
14.2 缓存击穿
缓存击穿是指一个热点 key 过期瞬间,大量请求同时回源。治理方法包括 proxy_cache_lock on、proxy_cache_use_stale updating、热点资源预热、TTL 增加随机抖动。
Nginx 能做缓存锁和 stale,但 TTL 随机抖动通常需要应用层或发布系统配合。
14.3 缓存雪崩
缓存雪崩是指大量缓存同一时间过期,导致 upstream 压力暴增。常见原因包括批量预热时设置了相同 TTL、发布后大量缓存同时生成、缓存目录被清空、Nginx 重启后缓存元数据恢复期间大量 MISS。
治理方法:不同资源使用不同 TTL,应用层增加随机过期时间,热点资源提前预热,保留 stale 能力,后端限流和降级,避免高峰期清空缓存。
15. 日志与缓存观测
15.1 增加缓存状态日志
建议在 access log 中加入缓存状态、upstream 时间和请求时间:
1 | log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" ' |
字段含义:
| 字段 | 含义 |
|---|---|
$request_time |
从收到请求到响应完成的总耗时 |
$upstream_connect_time |
与 upstream 建立连接耗时 |
$upstream_header_time |
upstream 返回响应头耗时 |
$upstream_response_time |
upstream 完整响应耗时 |
$upstream_cache_status |
缓存状态 |
$upstream_addr |
实际 upstream 地址 |
15.2 计算命中率
简单统计:
1 | awk '{for(i=1;i<=NF;i++) if($i ~ /^cache=/) print $i}' /var/log/nginx/access.log | sort | uniq -c |
如果日志格式中有 cache=HIT、cache=MISS,可得到类似:
1 | 12000 cache=HIT |
命中率可粗略按下面方式计算:
1 | HIT / (HIT + MISS + EXPIRED + BYPASS) |
但要注意:BYPASS 是否计入分母取决于分析目标;登录用户本来就不应该命中公共缓存;命中率高不代表一定正确,可能缓存了不该缓存的内容;命中率低也不代表一定不好,可能接口本身不适合缓存。
15.3 常见日志分析目标
| 目标 | 分析方式 |
|---|---|
| 看缓存是否生效 | 统计 $upstream_cache_status |
| 找慢请求 | 按 $request_time 排序 |
| 判断后端慢还是客户端慢 | 对比 $request_time 与 $upstream_response_time |
| 找 upstream 连接慢 | 看 $upstream_connect_time |
| 找大响应 | 看 $body_bytes_sent |
| 找异常状态码 | 统计 4xx、5xx |
| 找热点 URL | 按 $request_uri 聚合 |
| 找缓存污染 | 检查 Cookie、Authorization 与 HIT 是否同时出现 |
16. worker 与连接数调优
16.1 worker_processes
推荐:
1 | worker_processes auto; |
auto 会根据 CPU 核数设置 worker 数量。对于大多数场景,这是合理默认值。
不建议盲目设置成远大于 CPU 核数。过多 worker 可能增加上下文切换,并不能提升性能。
16.2 worker_connections
配置:
1 | events { |
理论最大连接数大致为:
1 | worker_processes * worker_connections |
但实际还受系统文件描述符限制、worker_rlimit_nofile、upstream 连接、keepalive 长连接等因素影响。反向代理场景中,一个客户端请求可能同时占用客户端连接和 upstream 连接,所以实际可承载客户端并发可能明显低于理论值。
16.3 worker_rlimit_nofile
配置:
1 | worker_rlimit_nofile 65535; |
同时需要系统层配合:
1 | ulimit -n |
systemd 管理的 Nginx 还要关注 service 配置:
1 | [Service] |
如果只改 Nginx,不改系统限制,可能仍然出现 too many open files。
16.4 accept_mutex
新版本 Nginx 在支持 EPOLLEXCLUSIVE 的系统上,惊群问题已经缓解。accept_mutex 是否需要开启要结合版本和系统。常见默认即可,不建议作为初学阶段重点调优项。
16.5 keepalive_timeout
客户端 keepalive:
1 | keepalive_timeout 65; |
含义:
keepalive_timeout:客户端长连接空闲保留时间。keepalive_requests:单个 keepalive 连接最多处理请求数。
设置过短会导致连接复用效果差,TLS 握手和 TCP 建连更多;设置过长会导致空闲连接占用 worker 连接数,高并发场景可能挤占资源。
| 场景 | 建议 |
|---|---|
| 普通 Web 站点 | 30-75 秒 |
| API 高频调用 | 30-60 秒 |
| 连接资源紧张 | 10-30 秒 |
| 长连接业务 | 单独 location 或服务治理 |
17. upstream keepalive 优化
反向代理到后端时,如果每次请求都重新建立 TCP 连接,会增加延迟和后端压力。
配置 upstream keepalive:
1 | upstream backend_api { |
关键点:
keepalive 64是每个 worker 保留的空闲 upstream 连接数,不是总连接数。- 必须使用
proxy_http_version 1.1。 - 通常要清空
Connection头,避免发送close。 - 后端也要支持 keepalive,并配置合理的空闲超时。
常见错误是只在 upstream 中写了 keepalive,但 location 里缺少 proxy_http_version 1.1 和 proxy_set_header Connection "",导致 upstream keepalive 没有真正生效。
18. 缓冲区与临时文件
18.1 proxy_buffering
proxy_buffering 控制 Nginx 是否缓冲 upstream 响应。默认通常是开启:
1 | proxy_buffering on; |
开启后链路是:
1 | upstream -> Nginx 缓冲 -> client |
好处是后端可以更快释放连接,慢客户端不会长期占用 upstream 连接,也有利于代理缓存。缺点是大响应可能写入临时文件,实时流式响应会被缓冲影响实时性。
18.2 适合关闭缓冲的场景
下面场景可能需要关闭:SSE、流式接口、大模型流式输出、实时日志推送、长轮询。
1 | location /stream/ { |
18.3 proxy_buffers
常见配置:
1 | proxy_buffer_size 16k; |
不要无脑调大。缓冲区按连接占用内存,高并发下会放大内存压力。估算方式是:
1 | 每连接缓冲占用 * 并发连接数 = 潜在内存占用 |
如果每连接缓冲 128KB,1 万并发理论上可能占用 1.28GB,还不包括其他连接、请求、缓存和系统开销。
18.4 临时文件目录
当响应大于内存缓冲区时,Nginx 可能写临时文件:
1 | proxy_temp_path /data/nginx/proxy_temp 1 2; |
如果磁盘 IO 慢,可能拖慢响应。需要关注临时目录所在磁盘性能、是否与缓存目录和日志目录共用磁盘、是否有足够空间、是否存在大量大响应。
19. Linux 内核参数基础
19.1 参数不是越大越好
内核参数调优要与机器规格、流量模型、Nginx 配置和后端能力匹配。下面参数是常见方向,不是无脑模板。
查看参数:
1 | sysctl net.core.somaxconn |
临时设置:
1 | sysctl -w net.core.somaxconn=65535 |
永久设置:
1 | vim /etc/sysctl.conf |
19.2 常见参数
| 参数 | 说明 | 关注点 |
|---|---|---|
net.core.somaxconn |
listen 队列上限 | 高并发建连场景 |
net.ipv4.tcp_max_syn_backlog |
SYN 队列长度 | 突发连接、SYN 半连接 |
net.ipv4.ip_local_port_range |
本地临时端口范围 | Nginx 主动连接 upstream 时重要 |
net.ipv4.tcp_tw_reuse |
TIME_WAIT 连接复用 | 新内核行为需核实 |
net.ipv4.tcp_fin_timeout |
FIN_WAIT 超时 | 连接释放速度 |
net.core.netdev_max_backlog |
网卡收包队列 | 高包量场景 |
fs.file-max |
系统级文件句柄上限 | 大量连接和文件访问 |
示例:
1 | net.core.somaxconn = 65535 |
19.3 临时端口耗尽
Nginx 作为反向代理主动连接 upstream 时,会使用本地临时端口。如果 upstream keepalive 不生效、短连接很多,可能出现端口压力。
检查:
1 | ss -ant | awk '{print $1}' | sort | uniq -c |
大量 TIME-WAIT 可能表示短连接很多。
优化方向:
- 开启 upstream keepalive。
- 扩大
ip_local_port_range。 - 后端连接池治理。
- 避免过短 keepalive。
- 检查是否频繁 reload 或连接被上游关闭。
20. 压测工具与方法
20.1 压测前准备
压测不是只跑一个命令。压测前要明确:
- 压测目标是什么:QPS、延迟、吞吐、带宽、缓存命中率还是连接承载。
- 压测对象是什么:静态资源、代理接口、缓存接口、HTTPS 入口还是大文件下载。
- 压测环境是否接近生产。
- 压测客户端是否足够强,是否成为瓶颈。
- 是否区分冷缓存和热缓存。
- 是否会影响生产环境。
- 是否有监控和日志记录。
20.2 wrk 示例
安装后执行:
1 | wrk -t4 -c200 -d60s http://static.example.com/assets/app.js |
| 参数 | 含义 |
|---|---|
-t4 |
4 个压测线程 |
-c200 |
200 个并发连接 |
-d60s |
持续 60 秒 |
关注输出:Requests/sec、Latency avg、Latency max、Transfer/sec、Socket errors、Non-2xx responses。
20.3 hey 示例
1 | hey -n 100000 -c 200 http://api.example.com/public/news/1 |
hey 适合快速 HTTP 压测,输出延迟分位数比较直观。
20.4 ab 示例
1 | ab -n 10000 -c 100 http://static.example.com/assets/app.js |
ab 简单易用,但能力较弱。学习阶段可以使用,生产压测建议结合 wrk、hey 或专业压测平台。
20.5 curl 单点验证
缓存验证:
1 | curl -I http://api.example.com/public/news/1 |
重点看:
1 | X-Cache-Status: MISS |
压缩验证:
1 | curl -I -H 'Accept-Encoding: gzip' http://static.example.com/assets/app.js |
缓存头验证:
1 | curl -I http://static.example.com/assets/app.js |
Range 验证:
1 | curl -I -H 'Range: bytes=0-1023' http://static.example.com/download/big.zip |
期望看到 206 Partial Content、Accept-Ranges: bytes、Content-Range。
21. 指标解读
21.1 请求指标
| 指标 | 含义 | 优化方向 |
|---|---|---|
| QPS | 每秒请求数 | 增加缓存、减少后端等待、优化连接 |
| 平均延迟 | 请求平均耗时 | 排查慢 upstream、网络、磁盘 |
| P95/P99 | 高分位延迟 | 更能反映用户体验和抖动 |
| 错误率 | 4xx/5xx 占比 | 区分客户端错误和服务端错误 |
| 吞吐 | 每秒传输数据量 | 带宽、压缩、大文件治理 |
| 命中率 | 缓存命中比例 | key、TTL、绕过规则、资源热度 |
平均延迟容易掩盖问题。生产中更应关注 P90、P95、P99 和最大延迟。
21.2 系统指标
| 指标 | 命令 | 关注点 |
|---|---|---|
| CPU | top、mpstat |
gzip、TLS、日志、正则 |
| 内存 | free -m、vmstat |
缓冲区、连接数、页缓存 |
| 磁盘 IO | iostat -x 1 |
缓存目录、临时文件、日志 |
| 网络 | sar -n DEV 1 |
带宽、包量、丢包 |
| 连接 | ss -ant |
ESTAB、TIME-WAIT、SYN-SENT |
| 文件句柄 | lsof、ulimit -n |
open files 上限 |
21.3 Nginx 日志指标
推荐同时观察:
1 | $request_time |
判断示例:
| 现象 | 可能原因 |
|---|---|
$request_time 高,$upstream_response_time 低 |
客户端慢、下载大文件、网络慢 |
$upstream_connect_time 高 |
upstream 建连慢、端口压力、网络问题 |
$upstream_header_time 高 |
后端处理慢 |
$upstream_cache_status 一直 MISS |
key 不稳定、绕过规则命中、TTL 太短 |
| HIT 仍然慢 | 磁盘 IO 慢、响应体太大、客户端慢 |
| 499 增多 | 客户端主动断开,可能服务慢或用户取消 |
| 502/504 增多 | 后端异常、超时、连接失败 |
22. 性能优化配置模板
下面模板适合作为学习和实验起点,不是直接复制到所有生产环境的万能配置。
1 | user nginx; |
23. 常见错误与排查
23.1 静态资源 404
排查方向:
root和alias是否拼接正确。- 文件是否真实存在。
- Nginx 用户是否有权限读取。
- 前端发布路径是否正确。
try_files是否把请求转到错误位置。- 是否被
open_file_cache_errors短时间缓存了 404。
命令示例:
1 | ls -l /data/www/app/assets/app.js |
23.2 gzip 不生效
排查方向:
- 请求是否带
Accept-Encoding: gzip。 - MIME 类型是否在
gzip_types中。 - 响应大小是否超过
gzip_min_length。 - 是否已经是压缩格式。
- 是否命中正确 location。
23.3 缓存一直 MISS
排查方向:
proxy_cache是否配置在当前 location。proxy_cache_path的keys_zone名称是否一致。- 缓存目录权限是否正确。
- 是否命中
proxy_cache_bypass。 - 是否命中
proxy_no_cache。 - 上游是否返回
Cache-Control: no-store、private或Set-Cookie。 - 请求 URL 是否每次都带随机参数。
- 缓存 key 是否包含不稳定头。
23.4 缓存了不该缓存的内容
这是严重问题。排查方向:
- 登录态 Cookie 请求是否被缓存。
Authorization请求头是否被缓存。Set-Cookie响应是否被写入缓存。- 缓存 key 是否缺少用户、语言、租户、地区等维度。
- 是否错误使用
proxy_ignore_headers。 - 是否把
/api/全部纳入缓存。
紧急处理流程:
1 | 立即关闭相关 location 缓存 |
23.5 worker_connections 调大后仍然报错
排查方向:
ulimit -n是否足够。- systemd
LimitNOFILE是否足够。 worker_rlimit_nofile是否配置。- 是否有大量 upstream 连接。
- 是否存在连接泄漏或慢客户端。
- 是否端口耗尽。
23.6 压测 QPS 上不去
排查方向:
- 压测客户端是否成为瓶颈。
- CPU 是否打满。
- 网络带宽是否打满。
- 磁盘 IO 是否高。
- access log 是否同步写入过多。
- 后端 upstream 是否慢。
- 缓存是否命中。
- TLS 握手是否成为瓶颈。
- gzip 压缩是否消耗 CPU。
24. 实验清单
24.1 静态资源缓存实验
目标:理解不同资源的缓存头。
步骤:
- 准备
index.html、assets/app.hash.js、assets/style.hash.css。 - 配置
/assets/长缓存。 - 配置
index.html不缓存。 - 使用
curl -I查看响应头。 - 修改 JS 文件名,模拟前端发布。
- 验证旧资源保留和新资源访问。
验收:
/assets/返回长缓存头。/index.html不被强缓存。- 前端路由刷新能回到
index.html。 - API 请求不会被 SPA 兜底错误处理。
24.2 gzip 实验
目标:理解 gzip 压缩收益和 CPU 成本。
步骤:
- 准备一个较大的 JS 或 JSON 文件。
- 开启 gzip。
- 使用 curl 分别请求压缩和非压缩版本。
- 对比文件大小。
- 使用 wrk 压测不同
gzip_comp_level。 - 观察 CPU 和吞吐变化。
验收:
- 文本资源返回
Content-Encoding: gzip。 - 图片资源不被 gzip。
- 压缩等级提高后 CPU 上升。
- 能说明压缩收益与 CPU 成本的关系。
24.3 proxy_cache 实验
目标:掌握代理缓存命中、过期、绕过和日志观测。
步骤:
- 准备一个后端接口,例如
/public/time。 - 配置
proxy_cache_path。 - 在
/public/location 开启proxy_cache。 - 增加
X-Cache-Status响应头。 - 第一次请求观察
MISS。 - 第二次请求观察
HIT。 - 等待 TTL 过期观察
EXPIRED。 - 增加
?nocache=1绕过缓存。 - 带
Authorization请求验证不缓存。
验收:
- 能稳定观察
MISS -> HIT -> EXPIRED。 - 能通过参数和请求头绕过缓存。
- 能解释
proxy_cache_bypass与proxy_no_cache区别。 - 能说明哪些接口不能缓存。
24.4 缓存锁实验
目标:理解热点缓存击穿。
步骤:
- 设置一个短 TTL 热点接口。
- 使用压测工具在缓存过期瞬间并发请求。
- 先关闭
proxy_cache_lock观察 upstream 请求数量。 - 再开启
proxy_cache_lock对比 upstream 压力。 - 观察 access log 中 cache 状态变化。
验收:
- 开启缓存锁后,上游并发回源明显减少。
- 能解释缓存击穿和缓存锁的关系。
24.5 sendfile 与大文件实验
目标:理解静态文件发送和 Range。
步骤:
- 准备一个 500MB 以上大文件。
- 开启
sendfile on。 - 使用 curl 下载并观察速度。
- 使用
Range请求验证断点续传。 - 配置
limit_rate_after和limit_rate。 - 对比限速前后效果。
验收:
- 大文件可以正常下载。
- Range 请求返回
206 Partial Content。 - 限速配置生效。
- 能解释大文件下载对带宽和连接数的影响。
24.6 worker 与文件描述符实验
目标:理解连接数与系统限制。
步骤:
- 查看
worker_processes和worker_connections。 - 查看
ulimit -n。 - 查看 systemd
LimitNOFILE。 - 使用压测工具逐步增加并发。
- 观察是否出现
too many open files。 - 调整系统和 Nginx 限制后再次压测。
验收:
- 能计算理论连接上限。
- 能说明系统文件描述符与 Nginx 配置的关系。
- 能定位
too many open files。
25. 生产实践建议
25.1 静态资源实践
- 带 hash 的静态资源使用长缓存。
- HTML 入口文件不要长缓存。
- 发布时先上传静态资源,再切换入口文件引用。
- 保留旧版本静态资源一段时间。
- 不要让 API 请求落到前端 SPA 兜底。
- 字体跨域只对必要资源开放。
- 大文件下载要评估带宽和限速策略。
25.2 代理缓存实践
- 默认不缓存动态接口。
- 只缓存明确的公共数据。
- 登录态、Cookie、Authorization 默认绕过缓存。
Set-Cookie响应默认不写缓存。- 5xx 默认不缓存,必要时使用 stale 兜底。
- 缓存 key 必须包含影响响应的关键维度。
- 添加
X-Cache-Status便于调试,但生产对外暴露需评估。 - 缓存 TTL 从短开始,验证稳定后再增加。
- 重要业务上线缓存前必须做灰度。
25.3 性能调优实践
- 每次只改一个关键参数,并记录结果。
- 压测要区分冷缓存和热缓存。
- 不要只看平均延迟,要看 P95/P99。
- access log 中保留 upstream 时间和缓存状态。
- 高并发前先检查文件描述符限制。
- gzip 压缩等级不要盲目设置到 9。
proxy_buffers不要无脑调大。- 内核参数调整要保留回滚方案。
- 生产压测要避开真实用户高峰,避免影响业务。
26. 学习检查清单
完成本阶段后,你应该能回答下面问题:
- Nginx 静态资源服务中
root和alias的路径拼接差异是什么? - 为什么前端
index.html不适合长缓存? - 为什么带 hash 的 JS/CSS 适合一年长缓存?
Cache-Control: no-cache和no-store有什么区别?- 强缓存和协商缓存分别发生在什么阶段?
- 304 表示什么?它是不是错误?
- gzip 适合压缩哪些资源,不适合压缩哪些资源?
gzip_comp_level为什么不是越高越好?- Brotli 为什么通常建议在 CDN 层优先开启?
sendfile解决了什么问题?tcp_nopush和tcp_nodelay分别适合什么场景?open_file_cache会缓存哪些内容?有什么副作用?proxy_cache_path中keys_zone和max_size分别表示什么?proxy_cache_key设计错误会导致什么问题?proxy_cache_bypass和proxy_no_cache有什么区别?- 为什么带 Cookie 或 Authorization 的请求默认不应该缓存?
proxy_cache_lock解决什么问题?proxy_cache_use_stale的收益和风险是什么?- 缓存穿透、缓存击穿、缓存雪崩分别是什么?
- 如何通过日志统计缓存命中率?
$request_time和$upstream_response_time如何帮助定位慢请求?worker_processes、worker_connections、worker_rlimit_nofile有什么关系?- 为什么调大
worker_connections后仍可能出现连接问题? - upstream keepalive 要如何配置才真正生效?
proxy_buffering off适合哪些场景?- 如何设计一次可靠的 Nginx 性能压测?
27. 过关标准
达到下面标准,可以认为第五阶段合格:
- 能为前端静态站点设计合理的缓存策略,避免发布后白屏和旧资源问题。
- 能为图片、字体、下载文件、HTML、JS、CSS 分别配置合适缓存头。
- 能正确开启和验证 gzip,并解释压缩收益与 CPU 成本。
- 能说明 Brotli 的价值、限制和部署方式。
- 能配置
sendfile、tcp_nopush、tcp_nodelay、open_file_cache并知道各自适用场景。 - 能设计一个安全的
proxy_cache方案,包含 key、TTL、绕过规则、缓存锁、stale 和观测头。 - 能避免缓存用户态、登录态、权限态、支付态等敏感响应。
- 能通过日志判断缓存命中、后端耗时、客户端耗时和慢请求来源。
- 能使用
wrk、hey、ab、curl做基础压测和验证。 - 能根据 CPU、内存、磁盘、网络、连接状态和文件描述符判断性能瓶颈。
- 能解释 Nginx 参数调优与系统参数之间的关系。
- 能形成“基线 -> 调整 -> 压测 -> 观测 -> 回滚或保留”的优化闭环。
28. 常见误区
- 认为 Nginx 性能优化就是复制一份高并发配置模板。
- 把所有资源都设置成长缓存,导致前端发布后用户无法更新。
- 不使用文件名 hash,却给 JS/CSS 配置一年缓存。
- 把登录态接口、用户信息接口、订单接口缓存到共享代理缓存。
- 缓存 key 只使用
$uri,忽略查询参数、Host、语言、租户等维度。 - 看到 304 就认为是异常。
- gzip 压缩图片、视频、zip 等已压缩资源。
- 把 gzip 压缩等级设置为 9,却不观察 CPU。
- 以为开启
keepalive后 upstream 连接复用就一定生效,忘记配置 HTTP/1.1 和Connection。 - 无脑调大
proxy_buffers,导致高并发时内存压力变大。 - 调大
worker_connections,但忘记系统文件描述符限制。 - 只看 Nginx QPS,不看后端、磁盘、网络和客户端瓶颈。
- 在生产高峰期清空缓存,导致大量请求同时回源。
- 把缓存命中率作为唯一目标,忽略数据正确性和安全性。
29. 下一阶段衔接
完成第五阶段后,你已经具备 Nginx 高级应用能力,能够设计高性能静态资源服务、基础代理缓存和连接调优方案。下一阶段将进入:
1 | 06 生产排障、高可用与运维体系 |
下一阶段重点不再只是“如何配置得快”,而是解决生产环境中更复杂的问题:
- 4xx、5xx、超时和连接异常如何系统排查。
- access log、error log、stub_status、指标和告警如何建设。
- reload、发布、回滚、灰度如何规范化。
- 多节点 Nginx 如何做高可用。
- Keepalived、LVS、云负载均衡和 DNS 如何配合。
- 生产事故如何复盘和沉淀。
第五阶段关注性能和资源效率,第六阶段关注生产稳定性和运维治理。两者结合后,才算真正具备守住生产 Nginx 的能力。
