用户登录

技术文章

外部链接将在新标签页打开。

Nginx 与 PHP-FPM 调优:先定测量项,再改配置

一句话回答

调优应从可观测项开始:分离 Nginx 连接层、FPM 进程池饱和与单请求耗时。进程池大小由可用内存除以单进程常驻内存峰值推算,而不是抄配置;超时与缓冲需成组设置,改完保留回滚并观察队列与 5xx。本文未在具体环境执行压测,所有取值为方法示例。

核心要点 TL;DR
  • 先测量再调参: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

七、改完如何验证#

  1. 保留上一版配置,明确回滚命令与生效方式(reload 与 restart 的差异要确认)。
  2. 灰度到部分实例,避免全量同时切换。
  3. 固定观察窗口,记录:FPM active/idle 进程数、listen queue、5xx 比例、慢日志条目速率、单进程 RSS。
  4. 若队列未下降而 CPU 已高,瓶颈不在进程数,应停止继续加 pm.max_children。
边界说明:本文没有联网核验任何版本行为,也未运行压测或配置检查。不同 Nginx / PHP 版本对上述指令的默认值与可用性可能不同,落地前请以你所装版本的官方文档与实际配置测试为准;无法确认的部分,标记为待核实,不要按示例值直接上线。

常见问题(FAQ)

Unix socket 一定比 TCP 快吗?
不一定。Unix socket 省掉一层 TCP 开销,但受 backlog 与同机部署限制;两者取舍应在自己环境压测,本文不给结论。
pm.max_requests 应该设多少?
它用于缓解进程内存缓慢增长,合适值取决于实际泄漏速率与重启代价,需要观察各自环境的 RSS 曲线,没有通用数字。

作者:沐小悠

本文约 6 分钟读完 · 关键词:Nginx 调优、PHP-FPM、pm.max_children、fastcgi、进程池

更多文章 GET 示例接口

「」标签下的其他文章