Nginx 调优别乱改,先把这几处默认值动对
线上跑着一个小接口站,白天还好,晚高峰 p95 从 80ms 跳到 400ms。翻日志没报错,CPU 也没满,第一反应往往是「Nginx 没调优」。于是有人把 worker_connections 从 1024 改到 65535,把 keepalive_timeout 从 65 拉到 300,重启后一压测,QPS 几乎没变。
我见过不少这样的配置,改的全是「看起来该改」的参数,真正卡住请求的那一项却原封不动。判断该动哪里,先得知道瓶颈在哪一层:是连接数打满、是 TLS 握手太贵、还是上游响应拖慢了整个 worker。三个场景对应的参数完全不同,一起改只会互相掩盖。
worker 与连接数:先算清楚这台机器要扛多少并发
worker_processes 设成 auto 基本没错,它按 CPU 核数起进程。真正的取舍在 worker_connections,这个值乘以 worker_processes 决定单机理论最大连接数。默认 1024 在老机器上够用,放到 8 核 16G 的机器上就明显偏小。
但把它拉到 65535 也不是答案。每个连接都要占内存,还要跟系统层的 ulimit -n 和 net.core.somaxconn 对齐,Nginx 这边放开了、内核没放开,多出来的连接照样被丢。我一般会先看 ss -s 里的实际连接数,再决定要不要往上调,而不是一上来就写最大值。
反向代理场景还要留意 upstream 的 keepalive。没配的话,Nginx 到后端每个请求都新建 TCP 连接,握手开销会吃掉不少吞吐。配置里加上 keepalive 64,配合 proxy_http_version 1.1 和 proxy_set_header Connection "",到上游的连接就能复用。
静态资源与 TLS:两个最容易被忽略的开销
如果站点以图片、JS、CSS 这类小文件为主,瓶颈往往不在并发数,而在磁盘 IO 和 TLS 握手。sendfile on 和 tcp_nopush on 能让内核直接把文件送进 socket,省掉用户态拷贝。这两个默认就开着,别去关。
TLS 是另一块。默认的 ssl_session_cache 是 none,意味着每个新连接都要完整握手一次。改成 shared:SSL:10m,再开 session_tickets,回访用户就能走会话复用,握手次数能降一大截。服务器是静态内容为主的站点,这项比调 worker_connections 见效快得多。
证书链别偷懒。中间证书没配全,部分客户端会额外发起一次请求去补,延迟凭空多出一个 RTT。这个坑在浏览器里不太显眼,在 API 调用方那边就是实打实的慢。
日志与超时:最该动手、最没人动手的一处
access_log 每写一条就是一次磁盘写。流量大的站点开着默认格式,日志 IO 能占掉相当一部分 CPU。我通常会把高频访问的静态路径用 map 单独标记,只记关键接口,或者直接上 buffer 攒批写盘。
超时参数是另一回事。client_body_timeout、send_timeout 默认 60s,proxy_read_timeout 默认也是 60s。上游偶尔慢一下,请求就挂满 60 秒,worker 被占着不放。按实际业务把上游超时压到 5~10 秒,快速失败比慢慢等更划算——反正用户也等不了那么久。
- 静态小文件为主:调 sendfile、TLS 会话缓存、日志缓冲
- 反向代理为主:调 upstream keepalive、proxy_read_timeout
- 高并发长连接:调 worker_connections,同时对齐内核参数
配置改完别急着上生产。用 ab 或 wrk 压一轮,对比改前改后的 p95,数据不动就说明改错了地方。我一般会先在测试机跑一轮,确认有效再推。
如果你手头的机器本身就在海外,连接建立和 TLS 握手的物理延迟已经摆在那,配置能省的是计算开销,省不掉物理距离。跑在硅谷机房的业务,选一台 硅谷裸机云 VI,E5-2620*2 配 32G 内存、100M 带宽,月付 $109,先在真实链路上压一轮,比在本地反复调参数更接近线上表现。
回到开头那个接口站,最后定位到的其实是上游数据库慢了,Nginx 侧只需要把 proxy_read_timeout 从 60 压到 8 秒,请求不再堆积。调优这件事,先量后调,顺序错了全是白费功夫。