Nginx 调优别急着改 worker_connections,先把文件句柄和 CPU 亲和理顺
先看瓶颈在哪,别一上来就抄参数模板
业务跑到某个量级,最先出问题的往往不是带宽,也不是 CPU 跑满。开着监控看,CPU 使用率只有 30%,可用户那边已经在报页面打不开了。这种「机器看着很闲、服务却在拒连」的状态,是最容易把人带偏的。
素材里那组数据很典型:QPS 超过 500 之后连接失败率飙到 37%,平均响应从正常的 50ms 涨到 215ms,而 CPU 只用了三成。真正的信号藏在两处——netstat -an | grep :443 | wc -l 输出 1024,正好卡在默认上限;error.log 里刷满了 worker_connections are not enough。
Nginx 性能优化这件事,我一般不建议从改配置开始。先确认瓶颈属于哪一层:是连接数撞墙、是文件句柄不够、是 worker 进程抢 CPU,还是后端接口本身慢。方向选错,后面改的每一个参数都是白改。
文件句柄 1024 这个默认值,是压死并发最常见的一根稻草
Linux 上一切皆文件,每一次用户请求都会占用一个文件句柄。系统默认给单进程 1024 个,这个数字放在今天的业务量面前基本等于没有。所以连接数一多,Nginx 不是处理不过来,而是根本没有资格再打开新的连接。
改法有三层,很多人只改了其中一层就以为完事,结果重启后又被系统拉回原形。
- 系统级:编辑 /etc/security/limits.conf,在文件末尾加上 soft nofile 65535 和 hard nofile 65535,root 用户和通配符 * 分别对应全局与单用户
- 进程级:在 nginx.conf 里写 worker_rlimit_nofile 35535,这个值最好与 ulimit -n 的结果对齐,否则两边不一致等于没设
验证别偷懒。ulimit -n 看的是 shell 当前限制,ps aux | grep nginx | awk '{print $2}' | xargs -I {} ls -l /proc/{}/fd | wc -l 看的才是 Nginx 进程实际打开了多少。两个数对不上,说明配置没生效。
这里有个取舍:把 nofile 拉到 65535 甚至更高,代价是内核要为每个句柄维护更多结构,内存占用会上去一点。但在 32G 内存的机器上,这点开销换来的是连接数不再撞墙,我认为非常值。
worker 进程和 CPU 亲和:核数不是越多越好,绑对才是关键
worker_processes 的经验值是 CPU 核心数,或者核心数乘二。查核数可以用 nproc,也可以 grep ^processor /proc/cpuinfo | wc -l。素材里那台机器只有 2 核,配置却是 worker_processes 1,等于让一半 CPU 全程围观。
但光把进程数调对还不够。进程在多个核之间来回迁移,缓存会反复失效,这部分损耗在压测里看不出来,在生产里却实实在在拖着响应时间。CPU 亲和就是把 worker 钉死在指定核心上。

- 4 核:worker_processes 4 配 worker_cpu_affinity 0001 0010 0100 1000
- 8 核:worker_processes 8 配 worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000
有个上限要记住:worker_processes 开到 8 个以上,性能基本不再提升,反而因为上下文切换变多而更不稳定。所以看到 64 核的机器就配 64 个 worker,是常见的过度配置。
顺带说一句,events 块里的 use epoll 和 multi_accept on 值得打开。epoll 在高连接数下的优势是明确的,而 multi_accept 让 worker 一次接受多个新连接,突发流量进来时排队现象会轻一些。
sendfile 和 tcp_nopush 能省掉一次内存拷贝,但它不是万能的
静态资源占比高的站点,sendfile on 几乎是必开项。它通过内核的 sendfile 系统调用把文件直接从内核空间送出去,省掉了数据在用户空间和内核空间之间来回搬的那一趟。文件越大、请求越密,省下来的开销越明显。
tcp_nopush on 要和 sendfile 配合使用,作用是把多个小包合并成一个再发,对大文件下载友好。反过来,实时性要求高的场景就不适合——WebSocket 这类长连接如果被攒包,延迟会变得很难看。
还有一个容易被忽略的层面:这些参数解决的是「单个请求处理得更快」,而连接数上限解决的是「同时能接多少请求」。两者不是一回事。素材里那台机器优化后平均响应从 215ms 降到 107ms,成功率从 63% 提到 85%,吞吐从 450 QPS 涨到 890 QPS,靠的是连接、句柄、worker 三处一起动手,单改 sendfile 到不了这个结果。
性能和安全要一起算账,防火墙规则不是越全越好
做优化的人容易钻进一个死角:只盯着 QPS 数字,忘了规则本身也在消耗资源。防火墙规则写得越细,每个包要过的判断就越多,这部分开销最终会体现在响应时间上。反过来,为了跑分把安全策略全关掉,隐患比性能收益大得多。
我的判断标准是看业务类型。电商抢购这种平时流量很低、到点瞬间暴涨的模式,优化重点应该放在突发连接的接纳能力上,而不是把常态下的每一个参数都拉到极限。日常流量平稳的站点,则更该关注单请求的处理效率。
硬件这一层同样有账要算。一台 2 核 32G 的机器,参数调到极限也就撑住那个量级;业务真到了需要 10 万并发的规模,靠改配置是改不出来的,得先换机器。像 美国西雅图高防服务器17 这种 E5-2620*2 / 32G / 100M 的配置,月付 $139.00,核数和内存都留了余量,适合把 Nginx 放在前面做接入层。如果静态资源体量大、出口带宽是瓶颈,美国硅谷超大带宽不限流量高配置服务器 IV 的 10G 口配 E5-2698v4*2 / 128G,月付 $1393.50,贵是贵,但省下的是 CDN 回源和用户等待的时间。

说白了,参数优化的天花板是硬件给的。先确认天花板在哪,再决定要不要花钱往上顶。
压测是唯一能证伪你判断的步骤
改完配置不压测,等于闭着眼睛调。ab -n 1000 -c 100 这种量级的压测,几分钟就能跑完,能直接告诉你成功率、平均响应时间和吞吐量有没有变化。
压测的时候注意看三个数:成功率、平均响应时间、QPS。三个数一起看才有意义,只看 QPS 上涨可能是靠牺牲成功率换来的。素材里优化前后的对比就是三组数一起给的,这种记录方式值得学。
另外,http_stub_status_module 这个模块打开后能看到当前连接数、总握手次数和总请求数,线上出问题时比翻日志快得多。日志分析适合事后复盘,实时状态适合当场判断。
最后给个可以直接执行的结论。2 核到 4 核的机器,先把 worker_processes 设成 auto 或核数,worker_connections 提到 2048 以上,worker_rlimit_nofile 与 ulimit -n 对齐到 65535,events 里开 epoll 和 multi_accept,静态资源站加 sendfile on 和 tcp_nopush on,然后压一轮看数据。8 核以上的机器,worker 数量不必跟着核数走,超过 8 个收益就很小了。业务量已经稳定超过单机承载、压测怎么调都上不去的时候,别再折腾参数,该换独立服务器了。