登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP-FPM pool 进程数与慢请求的配置思路

来源:17golang原创

时间:2026-10-17 12:48:50 174浏览 收藏

PHP-FPM pool 的进程数不能只看 CPU 核数:pm.max_children 决定同时处理请求的 worker 上限,实际值还要受可用内存、单个 PHP 请求峰值和慢请求占用时间约束。比较稳妥的做法,是先做一个范围清晰、能定位慢请求、也能及时终止失控请求的 pool 小配置,再按日志中的现象逐步调整。

官方地址:https://www.php.net/manual/en/install.fpm.configuration.php

先把“能同时处理多少请求”和“一个请求最多占用多久”拆成两个问题:前者由 pm 与 pm.max_children 管容量,后者由慢日志和请求终止参数管风险。只调大进程数,往往会把内存压力和排队时间一起放大。

先把 pool 的容量边界算清楚

这个小项目的目标,是为一个业务 pool 建立初始配置。PHP 官方文档把 pm 分为 static、dynamic 和 ondemand:static 固定 worker 数,dynamic 结合启动数和空闲数管理,ondemand 则在有请求时创建进程。三种模式都受 pm.max_children 限制。

实际落地时,可以先用“可分给 PHP-FPM 的内存 ÷ 单个 worker 峰值内存”估一个上限,再留出系统、Web 服务器和缓存的余量。例如不要把 4GB 机器直接配置成 200 个 worker;如果一个请求高峰约占 80MB,给 FPM 预留 2GB,计算出的理论值只有 25,还要继续为突发和其他服务留缓冲。这个数是容量起点,不是万能公式。

PHP-FPM master、pool、pm.max_children、动态进程和可用内存之间的静态结构关系说明图
图1:PHP-FPM pool 容量结构说明图,展示进程上限与空闲进程策略的关系;这是静态说明图,不是运行截图。
[www]
; 用 dynamic 表达常驻少量 worker、按负载补充的策略
pm = dynamic
; 这个值限制同时服务的请求数,先按内存预算给出保守上限
pm.max_children = 25
; 启动时先准备的 worker 数,不要高于 max_children
pm.start_servers = 4
; 低于这个空闲数时,FPM 会继续补充 worker
pm.min_spare_servers = 2
; 高于这个空闲数时,FPM 会回收多余 worker
pm.max_spare_servers = 8

如果流量长时间很低、请求间隔明显,ondemand 更容易控制空闲内存;如果流量稳定且希望减少创建进程的抖动,dynamic 更适合。不要同时用两套思路解释同一个 pool:pm.start_servers、pm.min_spare_servers 和 pm.max_spare_servers 只对 dynamic 有意义。

把慢请求和 worker 耗尽分开看

看到响应变慢时,先区分两类现象:如果 worker 都在处理长请求,新增请求会排队;如果单个脚本本身执行很久,增加 worker 只会让更多慢任务同时占资源。可以让访问日志记录耗时,再为慢请求单独打开 PHP backtrace。

FastCGI 请求、PHP worker、访问日志、慢日志和超时保护参数的静态结构关系说明图
图2:PHP-FPM 慢请求定位结构说明图,展示访问日志、慢日志和请求终止边界;这是静态说明图,不是运行截图。
; access.log 记录每个请求的耗时,便于区分排队与脚本慢
access.log = /var/log/php-fpm/$pool.access.log
access.format = "%R %t \"%m %r\" %s %{milliseconds}dms"

; 超过 5 秒时写入 PHP backtrace,slowlog 是定位入口,不是强杀开关
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/$pool.slow.log

; 单请求超过 30 秒就终止对应 worker,防止失控任务长期占槽
request_terminate_timeout = 30s

request_slowlog_timeout 负责把慢请求的调用栈写入 slowlog,request_terminate_timeout 才是单请求的终止边界。两者阈值不要设成同一个目的:前者要给排查留时间,后者要保护 pool。日志中若大量请求都接近终止阈值,应先查数据库、外部接口或锁等待,而不是盲目增加 pm.max_children。

用回收参数控制长期运行风险

如果第三方扩展或业务代码存在逐步增长的内存占用,可以给每个 child 设置处理请求数上限。pm.max_requests 达到后会让 child 重新生成,它更像缓慢泄漏的缓冲措施,不能替代修复泄漏源。

; 每个 worker 处理一定数量请求后重新生成,缓解长期增长的内存占用
pm.max_requests = 500

; 只允许 FPM 解析 PHP 脚本扩展,避免 Web 配置错误扩大解析范围
security.limit_extensions = .php

; 本机 Web 服务器优先通过 Unix socket 连接,减少暴露 FastCGI 端口
listen = /run/php/php-fpm-www.sock

如果改用 TCP 监听,必须把允许连接的地址收紧;PHP 官方文档还提醒,暴露 FastCGI 端点可能带来严重风险。pool 是资源隔离边界,不等于完整的安全隔离,部署时仍要把 socket 权限、Web 服务器用户和日志目录权限一起考虑。

形成可回滚的上线检查表

把配置放入版本库后,每次只调整一组参数,并保留变更前文件。上线前检查四件事:pm.max_children 没有超过内存预算;dynamic 参数之间没有互相矛盾;慢日志和访问日志目录可写;监听地址没有意外暴露。修改后通过发行版提供的 FPM 配置检查与服务重载流程确认配置能被接受,再观察访问耗时、慢日志数量和 worker 是否长期打满。

最终的判断顺序是:先看是否所有 worker 都被占用,再看慢日志定位到哪段代码,最后才决定提高上限、优化请求或缩短终止时间。这样调整出来的 pool,才是围绕真实瓶颈形成的容量配置,而不是把排队问题简单推给更多进程。

相关问题

pm.max_children 越大越好吗?

不是。它提高并发上限,也会提高潜在内存占用;单个请求很重时,过大的值会让系统进入交换或触发更严重的排队。

slowlog 能代替访问日志吗?

不能。slowlog 适合定位超过阈值的调用栈,访问日志适合观察每个请求的耗时和状态,两者配合才能判断是少量异常还是整体容量不足。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>