Nginx与PHP-FPM配置优化的通用思路,说到底就是一句话:让Nginx少等、让PHP-FPM少排队、让两者之间的连接别在关键时刻掉链子。具体怎么落地,下面从职责分工、参数计算、常见误区和验证方法几个层面拆开讲。
先弄明白Nginx和PHP-FPM各自管什么
不少运维新手一上来就翻参数手册,看到什么调什么,结果改完反而更慢。根子在于没搞清楚这两个组件在一条请求链路上分别扮演什么角色。
一个PHP请求是怎么走完的
用户访问一个PHP页面,Nginx先接住这个HTTP请求。如果请求的是图片、CSS这类静态文件,Nginx自己就返回了,根本不惊动PHP。只有当URL匹配到location ~ .php$这类规则时,Nginx才会通过FastCGI协议把请求转给PHP-FPM。
PHP-FPM这边有个master进程,它不处理具体请求,只负责管理一堆worker进程。worker进程才是真正执行PHP脚本的。执行完了,结果再沿着FastCGI连接回到Nginx,Nginx再回给浏览器。
这条链路上有三个地方容易卡:Nginx和PHP-FPM之间的连接建立与维持、PHP-FPM worker进程的数量够不够用、单个PHP脚本执行时间是不是拖得太长。配置优化基本就是围绕这三个卡点做文章。
优化的核心逻辑是减少等待和排队
行业共识认为,LNMP架构下多数性能问题不是某个参数没调对,而是资源分配和实际负载不匹配。比如PHP-FPM进程开太少,请求来了只能排队等worker空闲;开太多又把内存吃光,系统开始用swap,反而更慢。所以调优的第一步永远是搞清楚当前服务器的内存、CPU和实际并发量,而不是照搬网上的参数模板。
Nginx与PHP-FPM如何配置才能扛住高并发
高并发场景下,配置的重点从”能跑”变成”跑得稳”。这时候两个组件的配合方式比单个参数的大小更重要。
Nginx这边先做减法
Nginx本身很轻量,但默认配置里有些东西在高并发下会拖后腿。先确认几个基础项:
worker_processes设为auto,让Nginx自己按CPU核数决定,一般不需要手工指定。worker_connections决定单个worker能同时处理多少连接。这个值受系统ulimit -n限制,改Nginx配置前先确认文件描述符上限够不够。keepalive_timeout别设太长。长连接能减少握手开销,但占用连接槽位。一般60秒左右够用,静态资源多的站点可以适当延长。- 开启
gzip压缩文本类响应,但别对图片、视频做压缩,纯属浪费CPU。
还有一个容易被忽略的点:fastcgi_keep_conn on。默认情况下Nginx处理完一个PHP请求就断开和PHP-FPM的连接,下次请求再重新建。开启长连接后,连接可以复用,在高并发下能明显减少握手开销。不过这个参数需要PHP-FPM侧配合,两边版本都要支持。
PHP-FPM进程管理选static还是dynamic
PHP-FPM的pm参数有三种模式,选哪种取决于服务器用途和内存大小。
| 模式 | 特点 | 适用场景 |
|---|---|---|
| static | 固定进程数,启动就分配好,不动态增减 | 内存充足、流量稳定的生产环境,响应最快 |
| dynamic | 按负载动态增减进程,有 spare 进程保底 | 流量波动大、内存有限的场景,多数站点的默认选择 |
| ondemand | 有请求才起进程,空闲时几乎不占资源 | 访问量很低的小站或开发环境 |
生产环境如果内存够,static模式往往更稳,因为省掉了进程增减的判断开销,响应延迟更可控。但前提是pm.max_children算得准,设大了内存扛不住,设小了并发一上来就排队。
pm.max_children到底设多少
这个值不是拍脑袋定的。先看单个PHP-FPM进程实际占多少内存。在服务器上跑一下:
ps --no-headers -o rss -C php-fpm | awk '{sum+=$1} END {print sum/NR/1024 " MB"}'
这个命令算出所有php-fpm进程的平均常驻内存,单位是MB。假设结果是40MB,服务器总内存8GB,系统和其他服务占了2GB,留给PHP-FPM的大概6GB,那pm.max_children理论上可以设到150左右。但实际要留余量,设到120上下比较稳妥,给内存波动和系统缓存留空间。
如果是dynamic模式,还要配合pm.start_servers、pm.min_spare_servers、pm.max_spare_servers三个参数。经验做法是start设成max_children的四分之一左右,min_spare和start接近,max_spare不超过max_children的一半。具体数值要根据监控到的实际并发调整,没有一刀切的答案。
超时参数别乱设
Nginx侧的fastcgi_read_timeout和PHP-FPM侧的request_terminate_timeout要配合着看。前者是Nginx等PHP-FPM返回结果的最长时间,后者是PHP-FPM自己杀掉超时脚本的时限。
如果Nginx的超时比PHP-FPM短,会出现PHP还在跑但Nginx已经断开的情况,用户看到502,日志里却找不到PHP的错误。反过来,如果PHP-FPM先超时,Nginx会收到一个错误响应。建议让PHP-FPM的request_terminate_timeout略短于Nginx的fastcgi_read_timeout,这样PHP侧先做清理,Nginx再收尾,日志更清晰。
LNMP环境下PHP-FPM进程数怎么设置比较合理
这个问题在运维群里被问得特别多。很多教程直接给一个”max_children=50″之类的数字,但服务器配置千差万别,照抄基本等于碰运气。
先算内存再定进程数
合理的顺序是:先测单进程内存占用,再算可用内存,最后除以单进程内存得到上限。单进程内存不是固定值,取决于PHP加载的扩展和实际运行的代码。简单的CMS站点单进程可能只占20-30MB,跑着复杂框架和大量扩展的站点可能到80MB以上。
测的时候别在刚重启时测,那时候进程还没处理过真实请求,内存占用偏低。跑一段时间、有正常流量之后再采样,结果才靠谱。可以用上面那个ps命令多采几次取平均值。
动态模式下的参数配合
dynamic模式有几个参数需要一起看:
pm.max_children:进程池上限,按内存算。pm.start_servers:启动时预起的进程数,太少会导致刚启动时请求排队。pm.min_spare_servers:最少空闲进程,低于这个数就起新进程。pm.max_spare_servers:最多空闲进程,高于这个数就杀进程。pm.max_requests:单个进程处理多少个请求后重启。设这个是为了防止PHP内存泄漏累积,一般设500到1000,跑长时间任务的应用可以设低一些。
这几个值之间要满足min_spare_servers ≤ start_servers ≤ max_spare_servers ≤ max_children的关系,配错了PHP-FPM启动会报错。改完记得跑php-fpm -t检查语法。
用status页面看进程状态
PHP-FPM自带一个status页面,能看到当前活跃进程数、空闲进程数、监听队列长度等。在pool配置里加上:
pm.status_path = /phpfpm_status
然后在Nginx里配一个只允许内网访问的location指向这个路径。curl http://127.0.0.1/phpfpm_status就能看到实时数据。重点看listen queue,如果这个值持续大于0,说明进程不够用,请求在排队。如果max children reached频繁出现,也是同样的信号。
Nginx和PHP-FPM配置优化常见误区有哪些
网上流传的不少”优化秘籍”其实有坑,照着改可能当时看着还行,流量一上来就出问题。
worker_connections不是越大越好
有人把这个值调到几万,觉得连接数上限高就更抗压。但每个连接都占内存,而且实际能撑多少连接还受系统文件描述符限制。如果ulimit -n是1024,Nginx配置里写65535也没用,实际到1024就上不去了。正确做法是先调系统限制,再设Nginx参数,两者匹配才有意义。
盲目开fastcgi_cache
FastCGI缓存能把PHP执行结果缓存下来,对读多写少的站点效果明显。但缓存失效策略没配好的话,用户可能看到过期内容。而且缓存文件占磁盘,没配清理机制的话,跑几个月磁盘就满了。开之前想清楚哪些页面适合缓存、缓存多久、怎么清理,比直接打开开关重要得多。
调优顺序搞反了
有些运维先调PHP-FPM参数,再调Nginx,最后发现瓶颈其实在数据库或者磁盘IO上。合理的顺序是先确认瓶颈在哪:用top看CPU和内存,用iostat看磁盘,用Nginx的stub_status看连接处理情况。哪块先到瓶颈就先调哪块,别一上来就全量改配置。
国内服务器Nginx与PHP-FPM调优要注意什么
国内网络环境有个特点:DNS解析和跨运营商访问的延迟波动比较大。这对Nginx的上游配置有实际影响。
DNS解析别拖后腿
如果Nginx的proxy_pass或fastcgi_pass里写的是域名而不是IP,Nginx需要解析域名。默认情况下Nginx会缓存解析结果,但缓存时间受系统resolver配置影响。解析慢的时候,请求在等DNS上就耗掉不少时间。生产环境建议直接写IP,或者在本机hosts里配好映射,绕开DNS环节。
系统层面的配合不能少
Nginx和PHP-FPM调得再好,系统参数不配合也白搭。几个基础项:
ulimit -n调到至少65535,否则连接数上不去。- 内核参数
net.core.somaxconn影响监听队列长度,默认值偏小,高并发下要适当调大。 tcp_tw_reuse开启可以加快TIME_WAIT状态的端口复用,对短连接多的场景有帮助。- 如果PHP-FPM和Nginx不在同一台机器,注意内网带宽和延迟,FastCGI走公网基本没法用。
优化完怎么验证有没有效果
改完配置不验证,等于没改。最起码要做两件事:看监控指标、做简单压测。
盯住几个关键指标
- PHP-FPM status页面里的listen queue和max children reached,持续为0说明进程数够用。
- Nginx的
stub_status里active connections和requests per second,看连接处理有没有堆积。 - 系统层面的CPU idle和内存available,调优后不应该明显恶化。
- Nginx访问日志里的
request_time和upstream_response_time,对比看时间花在Nginx自身还是PHP处理上。
简单压测比感觉靠谱
用ab或wrk对几个典型页面做短时压测,看并发上去之后响应时间的变化曲线。重点不是追求多高的QPS数字,而是观察拐点在哪:并发加到多少时响应时间开始明显上升,那个位置基本就是当前配置的容量上限。调整参数后再压一次,对比拐点有没有后移。
据行业公开的运维实践总结,多数LNMP站点的性能问题在合理配置进程数和连接参数后都能得到明显改善,真正需要上缓存层或拆分服务的场景并没有想象中那么多。先把基础参数调对,再考虑架构层面的优化,顺序别颠倒。
Nginx与PHP-FPM配置优化常见问题解答
PHP-FPM进程数设多了会怎样
最直接的后果是内存不够用,系统开始用swap,PHP脚本执行变慢,反过来又占着进程不释放,形成恶性循环。另一个隐性问题是进程切换开销增加,CPU花在调度上的时间变多。所以进程数上限要按可用内存算,留出余量,不是越大越好。
Nginx和PHP-FPM分开部署可行吗
可以,但前提是两者之间走内网且延迟足够低。FastCGI协议对网络延迟比较敏感,跨公网或跨机房部署会导致每个请求多出几毫秒到几十毫秒的额外开销,高并发下累积起来很可观。如果确实要分离,优先考虑同机房内网互通,并且把连接超时参数适当调大。
改了配置需要重启还是重载
Nginx支持平滑重载,nginx -s reload即可,不断开现有连接。PHP-FPM也支持平滑重载,kill -USR2给master进程发信号,它会启动新的worker并逐步替换旧的。但涉及监听端口、用户权限这类核心参数变更时,平滑重载可能不生效,需要完整重启。改完配置先用nginx -t和php-fpm -t做语法检查,确认无误再操作。