Nginx 与 PHP-FPM 调优:先定测量项,再改配置
调优应从可观测项开始:分离 Nginx 连接层、FPM 进程池饱和与单请求耗时。进程池大小由可用内存除以单进程常驻内存峰值推算,而不是抄配置;超时与缓冲需成组设置,改完保留回滚并观察队列与 5xx。本文未在具体环境执行压测,所有取值为方法示例。
- 先测量再调参:FPM 的 listen queue 持续增长才说明进程池不足
- pm.max_children 用可用内存除以单进程常驻内存峰值推算,单进程内存需实测
- fastcgi_read_timeout、request_terminate_timeout 与缓冲要一起看,否则 50
在没有针对你所在环境的实测数据前,Nginx 与 PHP-FPM 调优的正确起点不是抄一份配置,而是先确定三层观察项:Nginx 的连接处理、FPM 进程池是否饱和、单个请求的时间花在哪里。本文给出的只是顺序与可核验项,不含性能数字,也没有在任何环境执行过压测;下文所有取值为方法示例,需按你自己的测量结果替换。
一、先确认瓶颈在哪一层#
三条观测路径通常够用,是否可用取决于模块是否已编译进去,需要逐项确认:
- Nginx 的
stub_status:看活跃连接与接受/处理连接的差值。 - FPM 的 status 页:通过
pm.status_path暴露,重点看 active / idle 进程与 listen queue。 - FPM 慢日志:
request_slowlog_timeout配合slowlog,定位是 PHP 执行慢还是排队慢。
推断(非实测结论):如果 listen queue 长期不为零且递增,说明请求在等进程,属进程池问题;如果进程空闲而耗时仍高,应转向下游依赖或 Nginx 与 FPM 之间的传输层。
二、进程池大小是算出来的#
常见做法是先用关系式估算上限,再用观测修正:
pm.max_children ≈ 可供 FPM 使用的内存 ÷ 单进程常驻内存峰值
其中单进程 RSS 峰值必须在你自己的负载下测,不能套用他人数字。pm 取值影响的是形状而非上限:static 适合流量平稳、内存预算固定的机器;dynamic 适合有明显波峰;ondemand 省内存,但进程冷启动的代价需自行评估。
三、Nginx 侧的连接预算#
worker_processes auto 是常见起点。连接数需要三处对齐:worker_connections、worker_rlimit_nofile,以及操作系统的文件描述符上限。作为反向代理时,一个客户端连接通常同时占用后端连接,容量估算要把这部分算进去。
四、通信路径:socket 还是 TCP#
Unix socket 省掉一层 TCP 开销,但受 listen.backlog 与同机部署约束;TCP 便于跨机部署与抓包观测,但有端口与栈开销。两者的 backlog 还要与内核参数匹配。哪个更快取决于请求大小与并发形态,应在自己环境压测后再定,本文不给定论。
五、超时与缓冲要成组设置#
三个参数分属两侧,必须一起考虑:
fastcgi_read_timeout(Nginx):应大于最慢正常请求的耗时。request_terminate_timeout(FPM):进程级硬上限,超过即被终止。fastcgi_buffering:关闭后慢客户端会一直占住 FPM 进程,可能放大进程池压力。
若两者关系没理顺,502 与 504 会混在一起,归因成本很高。
六、配置骨架(未执行示例)#
以下片段仅示意结构,占位值需替换,未在任何环境运行验证:
# nginx.conf 片段
upstream php_fpm {
server unix:/run/php/php-fpm.sock;
keepalive 16;
}
location ~ \.php$ {
fastcgi_pass php_fpm;
fastcgi_read_timeout 30s; # 需大于实测最慢正常请求
fastcgi_buffering on;
include fastcgi_params;
}
# php-fpm pool 片段
pm = dynamic
pm.max_children = 40 # 由内存除以单进程 RSS 峰值推算
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 16
pm.max_requests = 1000 # 缓解内存缓慢增长,值需观察
request_terminate_timeout = 60s
七、改完如何验证#
- 保留上一版配置,明确回滚命令与生效方式(reload 与 restart 的差异要确认)。
- 灰度到部分实例,避免全量同时切换。
- 固定观察窗口,记录:FPM active/idle 进程数、listen queue、5xx 比例、慢日志条目速率、单进程 RSS。
- 若队列未下降而 CPU 已高,瓶颈不在进程数,应停止继续加
pm.max_children。
边界说明:本文没有联网核验任何版本行为,也未运行压测或配置检查。不同 Nginx / PHP 版本对上述指令的默认值与可用性可能不同,落地前请以你所装版本的官方文档与实际配置测试为准;无法确认的部分,标记为待核实,不要按示例值直接上线。