切换语言
切换主题

Nginx 性能调优实战:gzip、缓存与连接池配置

上周接到报警,电商网站的首页加载时间飙升到 4 秒。打开 Chrome DevTools 一看,HTML 文件 120KB,CSS 加 JS 又是 350KB——全是未压缩的原始文件。更糟的是,每个请求都打到后端,缓存命中率只有可怜的 12%。当晚加完班,我花了 2 小时调整 Nginx 配置:gzip 压缩打开,缓存策略补齐,连接池参数调整。第二天早上再看,首页加载已经压到 1.6 秒,后端 QPS 降了快一半。

说实话,这类问题太常见了。很多人配 Nginx 就是装好就跑,gzip 默认不开,缓存随便写两行,连接数上限用默认值。结果一到流量高峰,服务器就喘不过气。

这篇文章把我在生产环境验证过的 Nginx 性能调优配置整理出来。gzip 压缩能减少 60-80% 的传输体积,换成 Brotli 还能再省 15-25%;缓存命中率 95% 时后端压力能少 90%;连接池配置合理的话并发能力能翻三四倍;再加上 Thread Pools 和 reuseport 这些进阶手段,单机 RPS 能冲到 50K-80K。我会把每个模块的配置细节、踩过的坑、实测数据都摊开讲清楚。

第一章:gzip/Brotli 压缩配置 — 减少传输体积

先说说 gzip 为什么这么重要。想象一下,你的 HTML 文件原始大小 100KB,通过 gzip 压缩后可能只有 20-25KB。这省下来的 75-80KB 带宽,对用户来说是更快的加载速度,对你来说是更低的流量成本。

我第一次意识到这个问题,是在一个客户的项目上。他们的移动端用户占比超过 60%,很多在 4G 网络环境下访问。首页加载要 3-4 秒,跳出率飙到 70%。加上 gzip 之后,传输体积直接砍掉 70%,首屏加载时间降到 1.5 秒左右。

1.1 gzip 基础配置:先让它跑起来

Nginx 的 gzip 配置其实不复杂,核心就几行:

http {
    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}

gzip on 是开关,这个不解释。gzip_vary on 很关键,它会在响应头加上 Vary: Accept-Encoding,告诉 CDN 和浏览器这个响应内容会根据客户端的压缩能力变化,避免缓存错乱。

gzip_min_length 设成 1000 字节,意思是小于 1KB 的文件不压缩。太小的文件压缩收益不大,反而浪费 CPU。gzip_types 指定要压缩的 MIME 类型,默认只压缩 text/html,你得把 CSS、JS、JSON、XML 这些都加上。

1.2 gzip 进阶配置:压缩级别和 MIME 类型清单

压缩级别是个需要平衡的选择。Nginx 的 gzip_comp_level 可以设 1-9,数字越大压缩率越高,但 CPU 消耗也越大。

我做过一组测试:

压缩级别HTML 压缩率CPU 时间(ms)推荐场景
165%2CPU 紧张
472%3平衡型(推荐)
675%5带宽紧张(推荐)
978%12极端场景

说实话,级别 4 和 6 是大多数情况下的最佳选择。级别 9 的 CPU 消耗翻倍,但压缩率只多几个百分点,不划算。

下面是我生产环境用的完整 gzip 配置:

# gzip 压缩配置
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/json
    application/javascript
    application/xml
    application/xml+rss
    application/xhtml+xml
    application/x-javascript;
gzip_disable "msie6";

gzip_proxied any 这个参数容易被忽略。如果你的 Nginx 作为反向代理,后端返回的响应头没有 Content-Length,默认不会压缩。设成 any 可以强制压缩所有符合条件的响应。

gzip_disable "msie6" 是为了兼容老版本 IE6,它对 gzip 支持有问题。现在 IE6 基本绝迹了,这行其实可以删掉,但我习惯留着,以防万一。

1.3 哪些文件类型收益最大?

根据我这边的实测数据,不同文件类型的压缩效果差异挺大:

文件类型原始大小压缩后压缩率
HTML100KB20-25KB75-80%
CSS80KB24-28KB65-70%
JavaScript120KB36-42KB65-70%
JSON API50KB20-25KB50-60%
图片/视频已压缩无意义0-5%

图片和视频本身就经过压缩(JPEG、PNG、MP4),再 gzip 反而会增大体积。所以 gzip_types 里千万别加 image/*video/*,加了就是负作用。

有一次我帮人排查问题,发现他们的 gzip_types 里加了 image/jpeg,结果图片体积反而增大了 3-5%。这种低级错误,我大概也犯过——以前不懂的时候,恨不得把所有 MIME 类型都加上。

1.4 Brotli:比 gzip 再省 15-25%

如果你想更进一步,Brotli 是个更好的选择。这是 Google 开发的压缩算法,在相同压缩级别下,比 gzip 压缩率高出 15-25%。特别适合静态资源,因为浏览器对 Brotli 的支持已经很广泛了。

不过有个坑:Brotli 不是 Nginx 默认模块,需要额外编译或安装动态模块。我用的官方动态模块方式,不用重新编译 Nginx:

# 需要先加载模块(如果用动态模块)
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

http {
    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css application/javascript application/json;
    brotli_min_length 256;
}

Brotli 的压缩级别也是 1-11,但我建议设 6。级别太高(比如 11)压缩时间会明显增加,对于动态生成的内容不太合适。静态文件可以提前用 Brotli 最高级别压缩,Nginx 直接返回预压缩的 .br 文件。

实测对比数据:

压缩方式100KB HTML 压缩后压缩耗时浏览器支持
gzip (level 6)25KB5ms几乎全部
Brotli (level 6)18KB15ms95%+
Brotli (预压缩 level 11)15KB095%+

我的建议是:动态内容用 Brotli level 4-6,静态资源用预压缩方式。如果 Nginx 编译太麻烦,gzip 也够用了,毕竟 75% 的压缩率已经很可观。

第二章:缓存策略配置 — 静态内容加速

缓存是性能调优里收益最直接的一环。配置得当的话,95% 的请求可以直接从 Nginx 返回,根本不用打到后端。我见过太多系统,后端服务器跑得满头大汗,结果 Nginx 那边缓存几乎没用——不是没开,是配错了。

2.1 proxy_cache 和 fastcgi_cache 怎么选?

Nginx 提供两种缓存机制:

  • proxy_cache:缓存上游服务器的响应,适用于反向代理场景(如 Node.js、Python、Go 服务)
  • fastcgi_cache:缓存 FastCGI 进程的响应,适用于 PHP-FPM 场景

选哪个看你的后端技术栈。如果用 PHP,就 fastcgi_cache;如果是 Node.js、Python、Go 这些,用 proxy_cache。两者配置逻辑几乎一样,我下面以 proxy_cache 为例讲解。

2.2 完整的 proxy_cache 配置

先在 http 块定义缓存路径:

http {
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;
}

逐行解释一下:

  • levels=1:2:缓存目录层级,1:2 表示两级目录结构,避免单目录文件过多
  • keys_zone=my_cache:10m:缓存区名称和元数据内存大小,10m 可以存约 8 万个缓存键
  • max_size=10g:缓存总大小上限,超过会按 LRU 算法淘汰
  • inactive=60m:60 分钟无人访问的缓存会被清理
  • use_temp_path=off:直接写入缓存目录,避免临时文件移动开销

然后在 server 或 location 里启用:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://backend;
        proxy_cache my_cache;

        # 缓存有效期配置
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_valid any 1m;

        # 缓存键设计
        proxy_cache_key $scheme$request_method$host$request_uri;

        # 降级策略
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

        # 响应头添加缓存状态(调试用)
        add_header X-Cache-Status $upstream_cache_status;
    }
}

proxy_cache_valid 是核心配置,定义不同状态码的缓存时长:

  • 200 302 10m:正常响应缓存 10 分钟
  • 404 1m:404 错误缓存 1 分钟,避免恶意请求狂打后端
  • any 1m:其他状态码缓存 1 分钟

2.3 缓存键设计和失效策略

缓存键 proxy_cache_key 决定了什么请求会被视为”相同”。默认是 $scheme$proxy_host$request_uri,但我建议显式声明:

proxy_cache_key $scheme$request_method$host$request_uri;

这样缓存键包含协议、请求方法、主机名和完整 URI。如果你的站点同时支持 GET 和 POST,或者有多个域名,这个配置会更精确。

缓存失效是个头疼的问题。常见的策略:

  1. 时间过期proxy_cache_valid 设置时间,到期自动失效
  2. 主动清理:用 proxy_cache_bypass 绕过缓存
  3. 缓存清除:商业版 Nginx Plus 有 proxy_cache_purge

我一般用第二种方案,通过请求头控制:

# 特定请求头绕过缓存
proxy_cache_bypass $http_x_nocache;

# 或者通过特定参数绕过
proxy_cache_bypass $arg_nocache;

需要刷新缓存时,加个 ?nocache=1 或请求头 X-Nocache: 1 即可。

2.4 降级策略:后端挂了也能服务

proxy_cache_use_stale 这个配置很实用。当后端服务出错或超时时,Nginx 可以返回过期的缓存内容,而不是直接报错。

proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;

去年双十一,我们后端服务扩容时出了点问题,API 服务间歇性 502。好在缓存配置了这个降级策略,用户访问基本没受影响——虽然缓存过期了几分钟,但内容还能正常返回。等后端恢复后,缓存自动更新。

实测数据对比:

指标无缓存缓存命中降级模式
响应时间150-200ms5-10ms5-10ms
后端 QPS1000500
用户体验正常正常略慢

缓存命中时,响应时间从 200ms 降到 5-10ms,快了将近 20 倍。这个收益太直观了。

2.5 微缓存模式:动态内容也能加速

很多人以为动态内容不能缓存,其实可以用微缓存(microcaching)。就是把动态页面缓存 1-5 秒,在高并发瞬间也能大幅减轻后端压力。

我在一个电商首页上试过这个方案。首页有实时推荐、库存显示,完全动态化。但大促期间流量暴增,后端扛不住。我加了 5 秒微缓存:

proxy_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=micro:10m max_size=1g;

location / {
    proxy_cache micro;
    proxy_cache_valid 200 5s;  # 只缓存 5 秒
    proxy_cache_lock on;        # 防止缓存更新时并发请求穿透
    proxy_cache_background_update on;  # 后台异步更新缓存
}

proxy_cache_lock on 很关键。当缓存过期时,第一个请求去后端取新数据,其他请求等着用旧缓存。这样就不会出现”缓存失效瞬间,大量请求同时打到后端”的情况。

效果惊人。首页 TTFB 从 800ms 降到 5ms 左右,后端 QPS 从 2000 降到 400。5 秒的缓存延迟用户几乎感知不到,但服务器压力减轻了一大截。

2.6 条件请求:省带宽的技巧

proxy_cache_revalidate 让 Nginx 用条件请求(If-Modified-Since / If-None-Match)去后端验证缓存是否过期。如果后端返回 304 Not Modified,就不用重新传输完整内容,只更新缓存元数据。

proxy_cache_revalidate on;

这个配置对带宽敏感的场景特别有用。比如你的后端返回大文件,但内容变化不频繁,用条件请求能省掉大量数据传输。

第三章:连接池配置 — 高并发场景必备

gzip 和缓存解决的是”怎么传输更快”,连接池解决的是”怎么承载更多请求”。默认配置下,单个 Nginx worker 最大并发只有 1024 个连接。流量一上来,直接不够用。

3.1 worker_connections:算清楚上限

最大并发连接数的计算公式:

最大并发 = worker_processes × worker_connections

假设你的服务器有 8 核 CPU,worker_processes 设为 8(或 auto 自动匹配),worker_connections 设为 4096:

最大并发 = 8 × 4096 = 32768

这个数字看起来很大,但要记住:每个请求通常占用两个连接(客户端到 Nginx,Nginx 到后端)。所以实际能处理的并发请求大概是这个数字的一半左右。

配置在 events 块:

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

use epoll 在 Linux 上是默认值,不需要显式写,但写上更清晰。multi_accept on 让 worker 同时接受多个新连接,在高并发场景下能减少连接排队。

3.2 客户端 keepalive:复用连接,减少开销

TCP 连接建立需要三次握手,开销不小。keepalive 让客户端和 Nginx 之间的连接可以复用,不必每次请求都重新建连。

http {
    keepalive_timeout 65;
    keepalive_requests 1000;
}

keepalive_timeout 65:连接保持 65 秒,超过就关闭。这个值不宜太大,否则会占用过多服务器资源;也不宜太小,否则复用效果不明显。60-75 秒是个合理区间。

keepalive_requests 1000:单个连接最多处理 1000 个请求。设得太小会频繁断连,设得太大可能导致资源泄漏。1000 是我测试下来比较稳妥的值。

还有个新参数 keepalive_time,控制单个连接的总存活时间(不管处理了多少请求)。如果你的服务器长时间运行,连接累积可能导致资源问题,可以加上:

keepalive_time 1h;  # 单个连接最多存活 1 小时

3.3 upstream keepalive:后端连接池

这个配置很多人不知道,但效果很明显。Nginx 和后端服务之间也可以复用连接,减少频繁建立 TCP 的开销。

upstream backend {
    server 127.0.0.1:8080;
    server 127.0.0.1:8081;

    keepalive 64;
    keepalive_timeout 60s;
    keepalive_requests 1000;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

keepalive 64:保持 64 个空闲连接池。这个值根据你的后端服务数量调整,一般设为后端服务器数量的 4-8 倍。

proxy_http_version 1.1proxy_set_header Connection "" 这两行必须加。HTTP/1.1 默认支持 keepalive,清空 Connection 头才能复用连接。不写这两行,upstream keepalive 不会生效。

实测效果对比:

配置连接建立次数/分钟CPU 开销推荐场景
无 upstream keepalive6000低流量
keepalive 323000中流量
keepalive 641500高流量

加了 upstream keepalive 后,连接建立开销直接砍掉 50%。这个配置在高并发场景下尤其重要。

3.4 文件描述符限制:别忘了系统层

Nginx 的连接数受限于系统的文件描述符上限。如果你的 worker_connections 设成 4096,但系统只允许每个进程打开 1024 个文件,那还是不够用。

检查当前限制:

ulimit -n

如果返回值小于 65536,需要在系统配置中提升。编辑 /etc/security/limits.conf

* soft nofile 65536
* hard nofile 65536

然后在 Nginx 配置中也声明一下:

worker_rlimit_nofile 65536;

这个配置放在 main 块(worker_processes 同级),让 Nginx 启动时就申请足够的文件描述符。

3.5 推荐参数速查表

我整理了一份不同场景下的推荐配置:

参数低流量(<1000 QPS)中流量(1000-5000 QPS)高流量(>5000 QPS)
worker_processesautoautoauto
worker_connections102420484096
keepalive_timeout606575
keepalive_requests1005001000
upstream keepalive163264
worker_rlimit_nofile4096819265536

这只是起点,实际调整需要结合压测数据。我一般会用 wrk 或 ab 做压测,看连接数和响应时间的变化曲线,找到最优值。

第四章:进阶优化 — Thread Pools 与 reuseport

前三章覆盖了大多数场景的优化。如果你的流量特别大(单机 RPS 目标超过 50K),或者对延迟特别敏感,这里还有两个高级配置可以尝试。

4.1 Thread Pools:突破 sendfile 的瓶颈

Nginx 默认是单线程事件驱动模型,这对大多数场景足够高效。但在高并发静态文件服务时,有个隐藏瓶颈:sendfile 虽然是零拷贝,但文件读取和 socket 写入都在同一个 worker 线程里完成。当磁盘 IO 慢时,会阻塞整个 worker。

Thread Pools 就是解决这个问题的。它把文件读取和发送拆到独立线程池,worker 只负责调度,不阻塞在 IO 上。

Nginx 官方博客有个测试案例:单机 9 倍性能提升。测试场景是 1MB 文件下载,原本受磁盘 IO 限制,加 Thread Pools 后直接突破瓶颈。

配置方法:

http {
    thread_pool default threads=32 max_queue=65536;
    aio threads=default;
    sendfile_max_chunk 512k;
}

threads=32 表示线程池 32 个线程,max_queue=65536 是最大排队任务数。aio threads=default 启用异步 IO 并指定用哪个线程池。sendfile_max_chunk 512k 控制每次发送的数据块大小,避免大文件占用太久。

不过 Thread Pools 不是万能药。如果你的后端是动态内容为主(API 服务),磁盘 IO 不是瓶颈,加了反而可能增加上下文切换开销。我的建议是:静态文件服务、大文件下载场景才考虑开启。

4.2 Socket Sharding (reuseport):降低连接延迟

Nginx 1.9.1 引入了 reuseport 参数,让多个 worker 可以独立监听同一个端口,避免 worker 之间争抢锁。

传统模式下,所有 worker 共享一个监听 socket,新连接来时要争抢 accept mutex。这在高并发时会有明显的锁竞争开销,延迟抖动。

加了 reuseport 后:

server {
    listen 80 reuseport;
}

每个 worker 有独立的监听 socket,内核自动把新连接分配给不同 worker。完全去掉了锁竞争。

实测数据(来源 Nginx 官方博客):

指标无 reuseport有 reuseport
平均延迟15.65ms12.35ms
延迟标准差3.5ms1.2ms
连接分布不均匀均匀

延迟降低 21%,更重要的是延迟抖动明显减少。这个配置在高并发场景(QPS 超过 20K)效果最明显,低流量场景可能感知不强。

4.3 open_file_cache:文件描述符缓存

静态文件服务场景,可以用 open_file_cache 缓存文件描述符和元数据,避免每次请求都去查磁盘:

http {
    open_file_cache max=10000 inactive=30s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;
}
  • max=10000:最多缓存 10000 个文件信息
  • inactive=30s:30 秒无人访问就清理
  • open_file_cache_valid 60s:每 60 秒校验一次缓存是否过期
  • open_file_cache_errors on:也缓存文件不存在等错误状态

这个配置对静态站点效果明显。但动态内容场景不建议用,因为文件可能频繁变化,缓存反而导致内容不更新。

第五章:综合配置模板 — 生产环境即用

前面四章讲了原理,这里直接给一个整合后的配置模板。你可以根据实际场景调整参数,但框架是通用的。

# nginx.conf 生产环境模板(高流量场景)

user nginx;
worker_processes auto;
worker_rlimit_nofile 65536;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    # gzip 压缩配置
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1000;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml
               application/xml+rss application/xhtml+xml;
    gzip_disable "msie6";

    # 缓存路径配置
    proxy_cache_path /var/cache/nginx
                     levels=1:2
                     keys_zone=my_cache:10m
                     max_size=10g
                     inactive=60m
                     use_temp_path=off;

    # 客户端连接配置
    keepalive_timeout 65;
    keepalive_requests 1000;
    keepalive_time 1h;

    # 文件缓存(静态站点可选)
    open_file_cache max=10000 inactive=30s;
    open_file_cache_valid 60s;
    open_file_cache_min_uses 2;

    # 后端服务器组
    upstream backend {
        server 127.0.0.1:8080;
        server 127.0.0.1:8081;
        keepalive 64;
        keepalive_timeout 60s;
        keepalive_requests 1000;
    }

    server {
        listen 80 reuseport;
        server_name example.com;

        location / {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Connection "";

            # 缓存配置
            proxy_cache my_cache;
            proxy_cache_valid 200 302 10m;
            proxy_cache_valid 404 1m;
            proxy_cache_key $scheme$request_method$host$request_uri;
            proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
            proxy_cache_revalidate on;

            # 调试用响应头
            add_header X-Cache-Status $upstream_cache_status;
        }
    }
}

三类场景的差异化配置

电商网站:首页和商品详情页变化频繁,缓存时间短一点,10-15 分钟足够。upstream keepalive 要大,因为数据库查询多,后端压力大。微缓存在大促期间可以开启,缓存 1-3 秒。

API 服务:数据实时性要求高,proxy_cache_valid 可能只有 1-5 分钟。gzip 对 JSON 效果好,务必加上。Brotli 如果能装就装,API 响应体积小但请求频繁,压缩收益可观。

静态站点:HTML、CSS、JS 几乎不变,缓存可以设到 1 小时甚至更长。gzip 压缩收益最大,静态文件文本多。open_file_cache 和 Thread Pools 可以开启,文件描述符缓存和异步 IO 对静态内容有明显帮助。

性能提升对比(更新版)

配置项优化前优化后提升比例
HTML 传输体积 (gzip)100KB25KB75% ↓
HTML 传输体积 (Brotli)100KB18KB82% ↓
API 响应时间(缓存命中)200ms8ms96% ↓
并发连接能力100040004x ↑
RPS (单机优化后)10K50K-80K5-8x ↑
TTFB (reuseport)15.65ms12.35ms21% ↓

这些数据是我实测和官方文档综合的结果。实际效果取决于你的服务器配置、网络环境和业务特点,压测验证很重要。

第六章:常见问题与排错

几个我遇到过的高频问题,直接给解决方案。

Q:gzip 压缩不生效,响应头没有 Content-Encoding: gzip

检查三个地方:

  1. gzip on 是否在正确的配置层级(http 块)
  2. gzip_types 是否包含你的响应 MIME 类型
  3. 响应体积是否大于 gzip_min_length

用 curl 测试:curl -H "Accept-Encoding: gzip" -I http://your-site.com

Q:缓存命中率很低,X-Cache-Status 大多是 MISS

常见原因:

  • 缓存键设计不合理,每个请求都被视为”不同”
  • proxy_cache_valid 设置太短,缓存还没用就过期了
  • 后端返回的响应头有 Cache-Control: no-cacheSet-Cookie

检查响应头,确认没有禁止缓存的指令。

Q:worker_connections 不够用,出现 502 错误

看 Nginx 错误日志,如果有 worker_connections are not enough,说明并发超过上限。

解决方案:

  1. 增加 worker_connections
  2. 检查是否有连接泄漏(keepalive 设置不合理)
  3. 考虑增加服务器数量做负载均衡

Q:gzip 和 sendfile 冲突?

不会冲突。gzip 压缩的是响应内容,sendfile 处理的是文件传输方式。两者可以同时启用。唯一要注意的是:gzip 对动态内容需要在内存中压缩,不走 sendfile;静态预压缩文件可以直接 sendfile。

Q:upstream keepalive 不生效?

两个常见原因:

  1. 漏了 proxy_http_version 1.1,HTTP/1.0 默认不支持 keepalive
  2. 漏了 proxy_set_header Connection "",不清空这个头,连接会被关闭

这两行必须加在 location 块里,不是 upstream 块。

Q:reuseport 报错 “duplicate listen options”?

reuseport 只能在 listen 行声明一次,不能在其他地方重复。确保每个 server 块的 listen 行最多出现一次 reuseport。如果多个 server 监听同一端口,每个都要独立加 reuseport。

Q:内存占用过高,服务器频繁 OOM

可能原因:

  • proxy_cache_pathkeys_zone 设太大
  • 缓存文件太多,内存映射占用高
  • keepalive 连接池太大,空闲连接占用资源

适当调小这些参数,或者给服务器加内存。


写在最后

说了这么多,其实 Nginx 性能调优的核心就是三件事:压缩、缓存、连接池。再加几个进阶手段(Brotli、微缓存、Thread Pools、reuseport),就能把单机 RPS 从 10K 提到 50K-80K。

调优不是一次做完就完事。我建议按这个路线走:

  1. 先开 gzip:改动最小,收益最直接,十分钟搞定
  2. 再配缓存:根据业务场景设计缓存策略,一天内能完成
  3. 然后调连接池:需要压测验证,适合流量稳定后做
  4. 最后试进阶优化:Thread Pools 和 reuseport,流量特别大才考虑

每次改动后,记得压测验证效果。wrk 或 ab 都可以,看响应时间、QPS、错误率的变化。别凭感觉调,用数据说话。

最后的检查清单,你可以对照执行:

  • gzip 已开启,MIME 类型配置完整
  • gzip_comp_level 设为 4-6,平衡 CPU 和压缩率
  • Brotli 已配置(如果可用)
  • proxy_cache_path 已配置,缓存大小合理
  • proxy_cache_valid 根据业务场景设置
  • 微缓存已开启(高并发动态内容场景)
  • proxy_cache_use_stale 降级策略已配置
  • worker_connections 设为 4096 或更高
  • keepalive_timeout 设为 60-75 秒
  • upstream keepalive 已配置(含 HTTP/1.1 和 Connection 头)
  • reuseport 已开启(高流量场景)
  • 文件描述符限制已提升(65536)
  • 响应头包含 X-Cache-Status 用于调试

差不多就这些。有问题可以评论区留言,我尽量回复。

常见问题

gzip 压缩不生效,响应头没有 Content-Encoding: gzip 怎么办?
检查三个地方:1) gzip on 是否在 http 块;2) gzip_types 是否包含响应 MIME 类型;3) 响应体积是否大于 gzip_min_length。用 curl -H "Accept-Encoding: gzip" -I 测试验证。
缓存命中率很低,X-Cache-Status 大多是 MISS 怎么排查?
常见原因:缓存键设计不合理、proxy_cache_valid 设置太短、后端返回 Cache-Control: no-cache 或 Set-Cookie。检查响应头确认没有禁止缓存的指令。
worker_connections 不够用,出现 502 错误如何解决?
查看 Nginx 错误日志,如果有 "worker_connections are not enough" 说明并发超限。解决方案:增加 worker_connections 值、检查连接泄漏、或增加服务器做负载均衡。
upstream keepalive 不生效的常见原因是什么?
两个常见错误:1) 漏了 proxy_http_version 1.1,HTTP/1.0 默认不支持 keepalive;2) 漏了 proxy_set_header Connection ""。这两行必须加在 location 块里。
Brotli 和 gzip 如何选择?
Brotli 压缩率比 gzip 高 15-25%,但需要额外安装模块。动态内容用 Brotli level 4-6,静态资源用预压缩 level 11。如果 Nginx 编译麻烦,gzip level 6 也够用,能达到 75% 压缩率。
不同流量场景的推荐配置参数是什么?
低流量(<1000 QPS): worker_connections 1024, keepalive 16;中流量(1000-5000 QPS): 2048, keepalive 32;高流量(>5000 QPS): 4096, keepalive 64。实际需结合压测数据调整。

18 分钟阅读 · 发布于: 2026年5月15日 · 修改于: 2026年5月15日

当前属于系列阅读 第 6 / 6 篇

Nginx 实战指南

如果你是从搜索进入这篇文章,建议顺手补上上一篇或继续下一篇,这样更容易把同一主题读完整。

查看系列总览

相关文章

BetterLink

想持续收到这个主题的更新?

你可以直接关注作者更新、订阅 RSS,或者继续沿着系列入口往下读,避免下次又回到搜索结果重新找。

关注公众号

评论

使用 GitHub 账号登录后即可评论