2、PHP-FPM配置调优:pm模式选择、进程池大小计算、请求超时设置
聊到PHP-FPM的配置,很多人第一反应就是改改pm.max_children。但说实话,这个参数怎么改、改成多少,背后是有门道的。我见过不少项目,服务器配置挺高,但PHP-FPM一压就挂,问题就出在进程管理策略上。
今天咱们就把pm模式、进程池大小、超时设置这三个核心点掰开揉碎讲清楚。嗯,都是实战中踩过的坑。
2.1 pm模式:static、dynamic、ondemand怎么选?
PHP-FPM提供了三种进程管理模式。选哪个,取决于你的业务场景。
2.1.1 static模式
说白了就是固定进程数。启动时就创建好指定数量的worker进程,一直保持。不会动态增减。
适用场景:服务器内存充裕、流量稳定、对响应时间要求高的场景。
我个人习惯在线上核心业务用static模式。为什么?因为它省去了进程创建和销毁的开销。每次请求进来,直接拿现成的worker处理,延迟最低。
但有个前提——你得算准进程数。设少了浪费资源,设多了内存撑爆。
2.1.2 dynamic模式
这是默认模式。PHP-FPM会根据负载动态调整worker数量。有几个关键参数:
pm.max_children:最大进程数pm.start_servers:启动时的进程数pm.min_spare_servers:最小空闲进程数pm.max_spare_servers:最大空闲进程数
dynamic模式的好处是灵活。低峰期进程少,节省内存;高峰期自动扩容。但问题也在这里——进程数频繁波动,CPU开销会增加。你想想看,每次创建或销毁进程,都是系统调用。
我曾经遇到过一个项目,dynamic模式下pm.max_spare_servers设得太大。结果流量一降,系统疯狂销毁进程,然后又因为某些监控请求重新创建。进程像过山车一样上下波动,CPU全耗在这上面了。
2.1.3 ondemand模式
这个模式更极端——有请求时才创建进程,请求处理完就销毁。空闲时不占用任何资源。
适合那种流量极低、甚至一天没几个请求的场景。比如一些内部管理工具、定时任务触发接口。
但注意,ondemand模式有冷启动问题。第一个请求进来时,需要先创建进程,响应时间会明显变长。我记得有一次帮朋友排查一个API接口,偶尔会超时,查了半天发现就是ondemand模式导致的。
| 模式 | 内存占用 | 响应延迟 | 适用场景 |
|---|---|---|---|
| static | 高(固定) | 低 | 高并发、稳定流量 |
| dynamic | 中等(动态) | 中等 | 流量波动较大 |
| ondemand | 低(按需) | 高(冷启动) | 极低流量、间歇性请求 |
2.2 进程池大小怎么算?
这是最核心的问题。进程数设多少,不是拍脑袋决定的。我一般按这个公式来估算:
max_children = (服务器总内存 - 系统预留内存 - 其他服务内存) / 单个PHP进程平均内存
举个例子。一台4GB内存的服务器,系统预留512MB,MySQL占用1GB,Redis占用256MB。剩下大约2.2GB给PHP。
单个PHP进程平均内存多少?这个得实际测。我习惯用pm.status_path配合脚本统计,或者直接看top命令。一般业务下,每个PHP进程大约20-40MB。
按30MB算:
max_children = 2200MB / 30MB ≈ 73
嗯,这里要注意。这个值是理论上限。实际生产环境,我建议留20%的余量。也就是设到58左右。
小技巧:用ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/NR/1024}'可以快速算出每个PHP进程的平均内存占用(单位MB)。
另外,别忘了考虑CPU核心数。进程数不是越多越好。当进程数超过CPU核心数的2-3倍时,上下文切换的开销会急剧增加。我建议:
- CPU密集型业务:进程数 ≈ CPU核心数 × 1.5
- IO密集型业务:进程数可以适当放大,CPU核心数 × 2~4
2.3 请求超时设置
超时设置是很多人容易忽略的点。但恰恰是它,能救你的服务器于水火之中。
2.3.1 request_terminate_timeout
这个参数是最重要的。它控制单个PHP请求的最大执行时间。超过这个时间,PHP-FPM会直接kill掉worker进程。
request_terminate_timeout = 30s
我个人习惯设30秒。为什么?因为正常的API请求,99%都在5秒内完成。超过30秒的,要么是死循环,要么是慢查询,要么是外部接口卡住了。与其让它耗着,不如直接干掉。
我曾经接手过一个项目,这个参数设成了0(不限制)。结果有个接口因为第三方API超时,导致PHP进程全部卡住。最后服务器内存耗尽,OOM killer开始乱杀进程。整个服务挂了20分钟。
2.3.2 request_slowlog_timeout
这个参数配合慢日志使用。设一个比request_terminate_timeout小的值,比如5秒。超过这个时间的请求,会被记录到慢日志中。
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log
这样你就能定位到哪些请求慢、慢在哪里。我每次排查性能问题,第一件事就是看慢日志。
2.3.3 max_execution_time
这是PHP本身的执行时间限制,在php.ini中设置。注意,它和request_terminate_timeout是两回事。
max_execution_time:PHP脚本层面的限制,不包括系统调用、IO等待等request_terminate_timeout:PHP-FPM层面的限制,包含所有时间
我建议把max_execution_time设得比request_terminate_timeout小一些,比如25秒。这样PHP自己先尝试终止,如果没成功,PHP-FPM再强制kill。
2.4 实战配置示例
最后,给出一份我常用的配置模板。假设是4核8GB的服务器,跑的是常规Web业务:
[www]
pm = static
pm.max_children = 80
pm.start_servers = 80
pm.min_spare_servers = 40
pm.max_spare_servers = 80
request_terminate_timeout = 30s
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log
; 安全相关
pm.max_requests = 1000
这里我用了static模式,因为流量相对稳定。pm.max_requests设1000,意思是每个worker处理1000个请求后自动重启。这是为了防止内存泄漏累积。
核心要点:配置没有银弹。每个项目都要根据实际负载、内存、CPU来调整。上线后持续观察,用pm.status_path或监控工具看进程数、内存变化,再微调参数。
嗯,关于PHP-FPM的调优,今天就聊这么多。下一章咱们讲OPcache配置,那个对性能提升更直接。