一、为什么FastCGI和PHP-FPM要和Apache搭伙?

很多做PHP开发的朋友,尤其是刚接触后端部署的新手,经常会被一堆名词绕晕:Apache是啥?FastCGI又是什么?PHP-FPM又是干嘛的?这仨为啥要凑到一起?其实咱们可以用开餐馆来类比理解。

假设你开了一家快餐店(就是你的PHP网站),顾客(用户请求)来买饭。如果没有中间环节,顾客得直接找厨师(PHP解释器)下单、等做饭、取餐,厨师还得同时负责招呼顾客、收钱、找零,那效率肯定低得要命,顾客一多厨师直接罢工。

这时候就需要几个分工明确的角色:Apache就是餐厅的前台服务员,专门负责接待顾客、登记订单、维持秩序,不用管做饭的事;FastCGI是餐厅的传菜通道,把前台的订单准确传给后厨,再把做好的菜端回前台,相当于一个标准化的通信管道;PHP-FPM就是后厨的厨师团队,专门负责处理订单(解释PHP代码),而且是成组的厨师(进程池),能同时处理多个订单。

如果这仨配合不好,比如厨师太少,顾客来了没人做饭;或者服务员太多但传菜通道太窄,订单传不进去;再或者服务员接了太多订单,通道都堵了,那整个餐厅肯定乱套。所以咱们调优的核心,就是让厨师团队、传菜通道、前台接待的能力刚好匹配,既不浪费人力,也不让顾客等太久。

二、先搞懂核心组件:FastCGI与PHP-FPM的分工

在调优之前,咱们得先把这俩的分工掰扯清楚,不然调优都是瞎调。

2.1 FastCGI:标准化的通信管道

FastCGI是一种通用的协议,不是专门给PHP用的,它的作用就是定义一套“请求怎么传、结果怎么回”的规则,不管你是用PHP、Python还是别的语言写后台,只要遵循这个规则,就能和前端的服务器(比如Apache、Nginx)通信。相当于餐厅的传菜通道有统一的尺寸、统一的传菜流程,不管是传汉堡还是传披萨,都按这个规矩来。

2.2 PHP-FPM:PHP专属的厨师团队

PHP-FPM是PHP专门开发的FastCGI进程管理器,相当于专门给PHP后厨定制的厨师团队管理系统。它会预先启动一批PHP解释进程(就是厨师),放在进程池里待命,不用每次顾客来都现招厨师(启动新进程),这样能省很多时间。

PHP-FPM有好几种管理进程池的模式,最常用的是动态模式(dynamic)、静态模式(static)和按需模式(ondemand),不同模式适合不同的场景,后面调优的时候会详细说。

三、PHP-FPM进程池参数调优:厨师团队的配置

PHP-FPM的核心配置文件是php-fpm.conf,里面最关键的就是进程池相关的参数,咱们可以把这些参数理解成厨师团队的配置:比如总共有多少厨师、最少留多少待命、最多能加多少临时厨师、厨师多久没活干就下班等等。

3.1 先看常用的进程池参数示例

咱们先拿一个完整的PHP-FPM配置示例,所有参数都加了注释,方便理解。这里明确标注技术栈为:PHP 7.4 + PHP-FPM。

; 进程池名称,相当于给厨师团队起个名字
[www]
; 监听方式:这里用Unix套接字,比TCP端口更高效,适合本地部署
listen = /var/run/php/php7.4-fpm.sock
; 监听权限,要和Apache的用户权限匹配,不然Apache没法传订单
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; 进程池管理模式:这里用动态模式,适合大部分中小网站
pm = dynamic
; 进程池最大进程数:相当于厨师团队最多能有多少人,不能超过服务器CPU核心数的1.5倍
pm.max_children = 10
; 最小空闲进程数:相当于最少留多少厨师待命,不能让顾客来了没厨师
pm.start_servers = 3
; 最大空闲进程数:相当于最多留多少待命厨师,多了浪费资源
pm.min_spare_servers = 2
pm.max_spare_servers = 4

; 进程最大请求数:每个厨师最多处理多少订单,处理完就下班,避免内存泄漏
pm.process_idle_timeout = 10s
pm.max_requests = 500

; 日志配置,方便排查问题
access.log = /var/log/php-fpm/www-access.log
error_log = /var/log/php-fpm/www-error.log

3.2 核心参数的调优逻辑

上面的配置里,pm开头的参数是核心,咱们得一个一个说清楚:

  1. pm(进程池管理模式):大部分中小网站选dynamic就够了,服务器资源充足的大型网站可以选static(固定进程数,启动慢但稳定),访问量极低的网站可以选ondemand(有请求才启动进程,省资源但响应慢)。
  2. pm.max_children(最大进程数):这个是最关键的参数,不能瞎设。如果设得太小,厨师不够用,顾客就得排队;设得太大,服务器内存不够,直接卡崩。一般的计算方法是:单进程内存占用 × max_children < 服务器可用内存。比如每个PHP进程占100M内存,服务器可用内存是1G,那max_children最多设10,也就是上面示例里的数值。
  3. pm.start_servers(启动进程数):相当于餐厅刚开门时先招多少厨师,不能比pm.min_spare_servers少,也不能比pm.max_spare_servers多,一般设成min和max的中间值就行。
  4. pm.min_spare_servers(最小空闲进程数):最少留多少厨师待命,比如你设2,就是哪怕餐厅没人,也得留2个厨师等着,避免顾客一来没厨师。
  5. pm.max_spare_servers(最大空闲进程数):最多留多少待命厨师,比如设4,要是餐厅没人,就把多余的厨师裁掉,省得占着工资。
  6. pm.max_requests(单进程最大请求数):每个厨师最多处理500单,处理完就下班,换个新厨师,避免厨师干久了累出毛病(内存泄漏),这个数值一般设500-1000就行。

四、Apache连接队列调优:前台服务员的配置

Apache的作用是接待顾客,所以它的配置得和PHP-FPM的厨师团队匹配,不然要么服务员太少接不过来,要么服务员太多传不进去订单。Apache的核心配置文件是httpd.conf,咱们先看一个完整的示例,技术栈标注为:Apache 2.4 + mod_proxy_fcgi(Apache和FastCGI的通信模块)。

; 启用mod_proxy和mod_proxy_fcgi模块,相当于开通传菜通道
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

; 配置MPM模式:Prefork模式适合PHP网站,稳定,单进程单线程
<IfModule mpm_prefork_module>
    ; 最大子进程数:相当于最多能有多少服务员,要和PHP-FPM的max_children匹配
    MaxRequestWorkers = 10
    ; 启动时的子进程数:相当于刚开门时的服务员数量
    StartServers = 2
    ; 最小空闲子进程数:最少留多少服务员待命
    MinSpareServers = 1
    ; 最大空闲子进程数:最多留多少服务员待命
    MaxSpareServers = 5
    ; 单进程最大请求数:每个服务员最多接多少单,处理完就下班
    MaxConnectionsPerChild = 500
</IfModule>

; 配置FastCGI连接:把PHP请求传给PHP-FPM
<FilesMatch \.php$>
    SetHandler "proxy:unix:/var/run/php/php7.4-fpm.sock|fcgi://localhost"
</FilesMatch>

4.1 Apache核心参数的调优逻辑

Apache的参数里,最关键的是MaxRequestWorkers(最大请求数),这个得和PHP-FPM的pm.max_children严格匹配。比如PHP-FPM最多有10个厨师,那Apache最多也只能有10个服务员,要是Apache设成20,就会有10个服务员接了订单没厨师处理,只能让顾客排队,甚至直接拒绝请求。

另外,Apache的MPM模式也很重要,PHP网站一般用Prefork模式,因为PHP解释器是单线程的,Prefork模式的单进程单线程刚好匹配,要是用Worker或Event模式,反而会出问题。

五、两者的匹配策略:让前台和后厨刚好配合

前面分别调了PHP-FPM和Apache,现在得把两者的参数匹配起来,不然还是会出问题。咱们分场景来说,不同的网站规模,匹配策略不一样。

5.1 中小网站(日活1000-10000)

这类网站的访问量不算太高,服务器资源也有限,一般是1核2G或2核4G的服务器。 匹配策略:

  1. 先算PHP-FPM的pm.max_children:用服务器可用内存除以单PHP进程的内存占用。比如1核2G的服务器,可用内存是1.5G,每个PHP进程占150M,那max_children设10,和之前的示例一致。
  2. Apache的MaxRequestWorkers设成和pm.max_children一样,比如10,这样服务员和厨师数量刚好匹配,不会有服务员没厨师的情况。
  3. PHP-FPM的pm.min_spare_servers设2,pm.max_spare_servers设4;Apache的MinSpareServers设1,MaxSpareServers设5,保证有足够的待命人员。

5.2 大型网站(日活10万以上)

这类网站的访问量很高,服务器资源充足,一般是8核16G以上的服务器。 匹配策略:

  1. PHP-FPM用static模式,固定进程数,比如设成CPU核心数的1.5倍,比如8核服务器设12,这样厨师数量固定,稳定。
  2. Apache的MaxRequestWorkers设成和pm.max_children一样,比如12,或者稍微多一点,比如15,因为大型网站的请求有波动,多几个服务员可以临时接待。
  3. PHP-FPM的pm.max_requests设成1000,Apache的MaxConnectionsPerChild设成1000,避免进程内存泄漏。

5.3 低访问量网站(日活1000以下)

这类网站的访问量极低,服务器资源紧张,比如1核1G的服务器。 匹配策略:

  1. PHP-FPM用ondemand模式,有请求才启动进程,省资源。
  2. Apache的MaxRequestWorkers设成5,不用太多服务员,因为访问量少。
  3. PHP-FPM的pm.process_idle_timeout设成5s,没活干的厨师很快下班,省内存。

六、应用场景、优缺点与注意事项

6.1 应用场景

这套调优方案适合所有用PHP开发的网站,比如博客、论坛、电商网站、企业官网等等。尤其是中小网站,资源有限,调优后能明显提升访问速度,减少服务器崩溃的概率。大型网站可以在此基础上做进一步的优化,比如用Nginx代替Apache,或者用多台服务器做负载均衡。

6.2 技术优缺点

优点:

  1. 分工明确,结构清晰,容易维护,新手也能快速上手。
  2. 调优灵活,根据不同的访问量和服务器资源,可以调整参数,适配不同的场景。
  3. 稳定性高,PHP-FPM和Apache都是成熟的技术,经过了长时间的验证,不容易出大问题。 缺点:
  4. 性能不如Nginx+PHP-FPM的组合,因为Apache的开销比Nginx大。
  5. 调优需要一定的经验,要是参数设得不对,反而会降低性能。
  6. 配置相对复杂,新手容易把参数设错,比如权限不匹配、监听方式不对等等。

6.3 注意事项

  1. 调优前一定要备份配置文件,要是调错了,可以快速恢复。
  2. 每次调优只改一个参数,改完后测试网站的访问速度和稳定性,再改下一个,不然出了问题不知道是哪个参数的问题。
  3. 要定期监控服务器的内存、CPU、进程数等指标,根据实际情况调整参数,比如访问量增加了,就要加大max_children和MaxRequestWorkers。
  4. 权限一定要匹配,Apache的用户(一般是www-data)要有权限访问PHP-FPM的监听文件,不然会出现503错误。
  5. 不要盲目照搬别人的配置,每个网站的访问量、服务器资源、PHP代码的复杂度都不一样,适合别人的不一定适合自己。

七、总结

FastCGI和PHP-FPM与Apache的集成调优,核心就是让前台服务员(Apache)、传菜通道(FastCGI)、后厨厨师(PHP-FPM)的能力刚好匹配,既不浪费资源,也不让用户等太久。调优的关键是搞清楚每个参数的作用,根据自己的网站规模和服务器资源,灵活调整,还要定期监控和测试,保证网站的稳定和高效。