Nginx调优别只改worker_processes:从并发上不去说起

2026-10-15 09:22 1012 次浏览

机房里有台机器,Nginx 跑静态站,压测到三千多并发就开始丢连接,CPU 看着才用了一半。运维第一反应是去改 worker_processes,从 4 调到 8,重启再看,曲线几乎没动。卡住的地方根本不在进程数上——报错日志里写的是 too many open files,句柄不够了。这类问题我碰得不少,方向错了,参数改一整天也是白改。

所以 Nginx 性能优化这件事,我的立场很明确:先定位瓶颈在哪一层,再决定是改配置还是加硬件。下面按硬件、系统、应用这个顺序拆开讲,每一层该动什么、不该动什么,以及什么时候调参数已经没用了、只能掏钱换机器。

worker_processes 开 8 个就到头,再往上加是浪费

进程数这块没什么争议。Nginx 的 worker_processes 一般设成 CPU 核心数,或者核心数乘以 2,不清楚核数就先看 grep ^processor /proc/cpuinfo | wc -l。但上限是 8 个——超过 8 个之后性能不会再提升,稳定性反而变差。这是经验值,不是理论推导,我一般直接按 8 封顶,除非机器核数确实离谱。

真正容易被忽略的是 CPU 亲和力。4 核就写 worker_cpu_affinity 0001 0010 0100 1000,8 核就是 8 位掩码依次排开,让每个 worker 绑死在一个核上,避免进程在核心之间来回迁移带来的缓存失效。这一项加上去,长连接场景下的抖动会小一截。

至于 worker_connections,单个 worker 能处理的客户端连接数,填 65535 就足够。实际并发上限是 worker 进程数乘以这个值,一个站能跑到这个量级,已经算大站了。连接数不够不是调这个参数能救的。

句柄和内核参数不改,连接数写多高都白搭

这就是开头那台机器卡住的地方。worker_rlimit_nofile 这个值,理论上应该是系统最大打开文件数除以 nginx 进程数,但请求分配并不均匀,所以直接和 ulimit -n 保持一致最省事。系统侧的 limits.conf 里针对用户或者用星号代表所有用户设好,重新登录才生效。

事件模型用 epoll,这个没什么可选的,Linux 上处理效率就是它最高。multi_accept 默认是 on,多个 worker 串行处理连接,一个连接只唤醒一个 worker;改成 off 之后变成并行,一个连接会唤醒所有 worker,直到分配完,没拿到的继续休眠。连接数不多的时候开着能降负载,吞吐量大的时候关掉换效率,这笔账要按实际流量算。

文件传输这块,sendfile on 是必须开的,静态资源走高效传输模式。但如果这台机器主要干下载,磁盘 IO 重,反而要把 sendfile 关掉来平衡磁盘和网络,代价是 CPU 占用上去一点。tcp_nopush 得在 sendfile 开启的前提下才有意义,作用是把响应头和正文开头合并发送,减少报文段数量。tcp_nodelay 则要包在 keepalive 参数里才生效,防的是网络阻塞。这三个参数不是全开就好,得看你是传小文件还是大文件。

静态资源缓存和 gzip,省下的是出口带宽的钱

open_file_cache 默认是关的,打开之后 max 建议和打开文件数一致,inactive 设 20s 表示文件多久没被请求就删缓存,open_file_cache_valid 30s 是检查缓存有效性的间隔。open_file_cache_min_uses 设 1,意思是 inactive 时间内用过一次的文件,描述符就一直留在缓存里。这套组合对静态站效果直接。

gzip 压缩是另一笔账。开启之后内容在发到客户端之前先压,省出口带宽、加快传输。gzip_http_version 设 1.1,老浏览器识别不了压缩的用 gzip_disable 过滤掉。这两项配好,同样的带宽能扛更多访问量。

不过压缩也吃 CPU。如果机器本身 CPU 已经吃紧,压得越狠反而越慢,这个取舍得看你的瓶颈是在带宽还是在算力。带宽是瓶颈就开,CPU 是瓶颈就得掂量。

缓存和超时参数也顺手理一遍:client_header_buffer_size 4k 一般够用,按系统分页大小设;client_max_body_size 管上传大小限制;client_header_timeout、client_body_timeout 都可以设低些,超时没数据就返回 request time out,把资源让出来。reset_timeout_connection 关掉不响应的连接,释放它占的内存。server_tokens off 不会让 Nginx 更快,但能藏掉错误页里的版本号,安全上划算。

代理到后端 FastCGI 的,fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout 这几个 600 秒的超时要按业务实际调,缓冲区 fastcgi_buffer_size 64k、fastcgi_buffers 4 64k 按页面大小定。一个 PHP 页面 256KB,正好 4 个 64KB 缓冲区装下;页面更大就会落盘到 fastcgi_temp_path,内存处理比磁盘快,所以缓冲区该给够。

参数调到头还上不去,就该看硬件和带宽了

配置这一层能榨出来的性能是有上限的。当你把 worker_processes 顶到 8、worker_connections 填到 65535、句柄和内核参数都对齐了,并发还是卡在某个数字上,问题就转移到硬件层面:CPU 主频、磁盘 IO、网卡是不是丢包、网络有没有延迟。

静态资源吃磁盘空间和 IO,动态请求吃 CPU 和内存。做代理的话,瓶颈往往在 CPU 上。这些不是改配置文件能解决的,得换机器。大带宽场景下,出口带宽本身就是硬约束,配置再漂亮也变不出带宽来。

比如香港机房做静态资源分发或者代理入口,带宽和线路质量直接决定并发天花板,这种场景我会优先看大带宽机型。秀米云的 香港大带宽服务器 XVIII,Platinum-8168*2 配 64G 内存、30M 带宽,适合代理和静态站混跑;如果并发压力更大、需要更足的带宽余量,美国硅谷超大带宽不限流量高配置服务器 IV 走 E5-2698v4*2、128G 内存、10G 带宽,出口这块基本不会成为短板。这两台的定位不一样,前者看线路和延迟,后者看吞吐量,按业务面向的用户群选。

判断顺序我一直是这么走的:先看日志和 stub_status 确认连接情况,再低峰期压一轮测出当前系统的真实承载,找到瓶颈在哪一层,然后才动手。跳过定位直接改参数,改对了是运气,改错了还以为是配置没生效,来回折腾的时间比机器本身值钱。

结论给直接点。单机小站,把 worker_processes 设成核数、句柄对齐、sendfile 和 gzip 开上,基本就够了,不用追求参数全调满。静态资源量大、并发上万、带宽吃紧的,配置调完还是不够,就该考虑上大带宽物理机,香港机型月付四百多美元起,美国硅谷那台千元出头美元档,按并发和出口需求挑。纯动态小接口、流量平稳的业务,别急着换硬件,先老老实实把 FastCGI 超时和缓冲区调对,往往比换机器见效快。