FastCGI 和 PHP-FPM 到底是什么关系

这题在 PHP 面试里非常常见,但也非常容易答混。

很多人会把这几个概念混在一起:

  • CGI
  • FastCGI
  • PHP-FPM
  • Nginx

结果就是:

  • 只会说一句“PHP-FPM 是 PHP 解释器”
  • 或者说“FastCGI 就是 PHP-FPM”
  • 再或者只背 Nginx + PHP-FPM

这些都不算真正讲清楚。

如果你想把这题答得更像“做过线上系统的人”,最好把它拆成三层:

  1. 它们各自是什么
  2. 它们怎么协作
  3. 出问题时怎么分析和解决

一、先把 3 个概念拆开

1. CGI 是什么

CGI,Common Gateway Interface,本质上是一套“Web Server 怎么把动态请求交给外部程序处理”的老方案。

你可以粗暴理解成:

  • Web Server 收到一个动态请求
  • 临时启动一个外部进程
  • 把请求参数、环境变量、标准输入传给它
  • 外部程序处理后,把结果写回标准输出
  • Web Server 再把结果返回给客户端

CGI 的优点

  • 模型简单
  • 语言无关
  • 任何能读写标准输入输出的程序都能接进来

CGI 的核心问题

每来一个请求都要新建一个进程,成本太高。

这会带来几个问题:

  • 频繁 fork / exec
  • 进程创建开销大
  • 并发一高就很容易顶不住

所以 CGI 的问题不是“不能用”,而是:

它在高并发 Web 场景下太重了。


二、FastCGI 是什么

FastCGI 可以理解为 CGI 的改良版。

它要解决的核心问题就是:

不要每个请求都临时起一个新进程。

FastCGI 的核心思想

  • 外部程序常驻
  • Web Server 不再为每个请求反复创建进程
  • 双方通过一套协议长期通信

也就是说,FastCGI 不是某个具体软件,而是一种协议和工作方式。

你可以这样记

  • CGI:请求来了,临时起进程
  • FastCGI:进程提前常驻,请求来了直接交给它处理

FastCGI 解决了什么

  • 减少进程创建销毁开销
  • 提升吞吐
  • 更适合高并发 Web 动态请求

面试里一句很稳的话

FastCGI 本质上是 CGI 的常驻进程化版本,它解决的是“每个请求都起新进程”带来的性能问题。


三、PHP-FPM 是什么

PHP-FPM 全称是 PHP FastCGI Process Manager

这句话里最关键的不是 PHP,而是:

FastCGI Process Manager

也就是说,PHP-FPM 不是 FastCGI 协议本身,而是:

PHP 在 FastCGI 模式下的进程管理器。

它负责什么

  • 维护一组常驻 PHP worker 进程
  • 管理这些 worker 的创建、回收和数量上限
  • 接收来自 Nginx 的 FastCGI 请求
  • 把请求分配给空闲 worker 去执行 PHP 脚本

它不负责什么

  • 不负责静态资源分发
  • 不负责 HTTPS 连接接入
  • 不负责反向代理
  • 不负责业务路由

这些一般是 Nginx 做的。

所以三者关系一句话怎么说

FastCGI 是协议,PHP-FPM 是 PHP 的 FastCGI 进程管理器,Nginx 是请求入口和反向代理。

这句一说出来,概念就清楚很多了。


四、Nginx、FastCGI、PHP-FPM 到底怎么协作

很多人会背:

客户端 -> Nginx -> PHP-FPM

这不算错,但还不够细。

更完整的链路应该是:

  1. 客户端把 HTTP 请求打到 Nginx
  2. Nginx 判断这是静态资源还是 PHP 动态请求
  3. 如果是静态资源,Nginx 直接返回
  4. 如果是 PHP 动态请求,Nginx 按 FastCGI 协议把请求转发给 PHP-FPM
  5. PHP-FPM 选一个空闲 worker 执行对应 PHP 脚本
  6. worker 执行完成后,把响应内容返回给 PHP-FPM
  7. PHP-FPM 再把结果回给 Nginx
  8. Nginx 最终把 HTTP 响应返回给客户端

这里最容易被问到的点

1. Nginx 为什么不直接执行 PHP

因为 Nginx 本身不是 PHP 解释器,它擅长的是:

  • 网络连接处理
  • 高并发事件驱动
  • 静态资源处理
  • 反向代理

而执行 PHP 脚本,需要 PHP 运行时和进程池管理,这正是 PHP-FPM 的职责。

2. FastCGI 在这里扮演什么角色

FastCGI 是 Nginx 和 PHP-FPM 之间通信的语言。

你可以把它理解成:

  • HTTP 是浏览器和 Nginx 说话的协议
  • FastCGI 是 Nginx 和 PHP-FPM 说话的协议

五、PHP-FPM 内部到底是怎么工作的

这块才是面试官最爱深挖的地方。

1. PHP-FPM 的基本结构

PHP-FPM 一般有两类进程:

  • master 进程
  • worker 进程

master 进程负责什么

  • 读取配置
  • 监听端口或 Unix Socket
  • 创建和回收 worker
  • 平滑重载
  • 监控 worker 状态

worker 进程负责什么

  • 真正执行 PHP 脚本
  • 处理具体请求

一个 worker 在同一时刻通常只处理一个请求。

这句话非常重要,因为它解释了很多线上现象:

  • 为什么并发一高,FPM 容易打满
  • 为什么 max_children 很关键
  • 为什么单个慢请求会占住一个 worker

六、为什么 PHP-FPM 是进程池,不是线程池

你可以从历史和稳定性两个角度讲。

1. PHP 的经典 Web 模型更适合多进程隔离

PHP 很长一段时间的经典模型就是:

  • 一个请求进入
  • worker 执行脚本
  • 请求结束后释放大部分运行态

这种模型天然适合通过多个独立进程来隔离请求。

2. 多进程隔离更稳

如果某个 worker 内存泄漏、崩溃、卡死,通常只影响这个 worker,不会把整个服务一起拖死。

3. 这也解释了它的边界

多进程模型虽然稳,但也意味着:

  • 进程切换有成本
  • 内存占用比线程模型更高
  • 共享内存状态不自然

所以 PHP-FPM 的重点不是“单机极致吞吐”,而是:

稳定、可控、隔离性强。


七、pm = static / dynamic / ondemand 到底怎么理解

这是 PHP-FPM 面试里特别爱问的点。

1. static

固定数量的 worker,启动后一直常驻。

特点:

  • 响应稳定
  • 没有临时拉起进程的抖动
  • 内存占用可预估

适合:

  • 流量相对稳定
  • 机器资源比较充足

2. dynamic

启动一部分 worker,再根据压力在 min_spare_serversmax_children 之间动态伸缩。

特点:

  • 更平衡
  • 线上最常见

适合:

  • 大多数常规业务系统

3. ondemand

有请求才拉 worker,空闲久了再回收。

特点:

  • 节省内存
  • 首次请求可能有拉起开销

适合:

  • 低频服务
  • 长时间空闲的业务池

面试里怎么一句话讲清

static 更偏稳定,ondemand 更偏省资源,dynamic 是大多数线上系统的折中选择。


八、请求到底是怎么到 worker 的

这块如果能讲清,面试官一般会觉得你不是只背词。

一个请求进来时,大致会发生什么

  1. Nginx 把 PHP 请求通过 FastCGI 发给 PHP-FPM
  2. PHP-FPM 把请求放到监听队列
  3. 空闲 worker 从队列里取到请求
  4. worker 初始化请求上下文
  5. worker 执行目标 PHP 脚本
  6. 业务代码跑完,生成输出
  7. 输出返回给 Nginx
  8. 当前 worker 回到空闲状态,等下一次请求

这就引出两个关键点

1. 为什么慢请求会拖垮 FPM

因为慢请求会长时间占住 worker。

如果并发请求越来越多,但每个请求都执行得很慢,就会出现:

  • 空闲 worker 越来越少
  • 新请求排队
  • 队列堆积
  • 最后出现超时或 502

2. 为什么 max_children 打满不一定是参数问题

因为真正的根因往往是:

  • SQL 慢
  • Redis / MySQL / HTTP 外部依赖慢
  • 代码里有重逻辑
  • 下游超时重试导致雪崩

max_children 只是并发承载上限,不是性能优化本身。


九、线上为什么会出现 502max_children 打满、队列堆积

这块很适合面试拉开差距,因为它从“背概念”进入“做过排查”。

1. 502 Bad Gateway

常见含义是:

  • Nginx 没有从 PHP-FPM 拿到正常响应

常见原因:

  • PHP-FPM 没启动
  • listen 的 socket / 端口不通
  • worker 打满,排队过久
  • worker 执行超时或崩溃
  • 脚本 fatal error,导致返回异常

这时候该怎么查

先查:

  • Nginx error log
  • PHP-FPM log
  • PHP slow log
  • 当前 worker 数和 max_children

而不是一上来就改配置。

2. max_children reached

这个日志的本质是:

当前可用于处理请求的 worker 全被占满了。

这意味着:

  • 请求进来的速度,大于请求处理完成的速度

根因可能是:

  • 突发流量
  • 慢 SQL
  • 第三方接口慢
  • 某段代码 CPU 特别重
  • 机器资源不足

解决思路

不要只说“调大 pm.max_children”。

更稳的回答应该是:

  1. 先确认是不是短时流量波峰
  2. 再看慢请求到底卡在数据库、缓存还是外部接口
  3. 再评估机器内存是否支持更高的 worker 数
  4. 最后再决定要不要调大 max_children

3. listen queue 堆积

这说明请求已经到 PHP-FPM 入口了,但没有足够快地被 worker 消化。

本质上还是:

  • 供给和消费失衡

它通常说明问题不只是“配置小”,更可能是:

  • worker 处理太慢
  • worker 不够
  • 上游流量突增

十、面试里怎么从“分析”讲到“解决”

这块非常关键。

很多人的回答停在:

PHP-FPM 打满了,就调大 max_children。

这个回答太薄。

更好的回答顺序是:

  1. 先判断是流量问题还是执行变慢
  2. 看 PHP-FPM 是否真的打满
  3. 看慢请求卡在哪一层
  4. 再决定是扩 worker、优化 SQL、加缓存,还是拆异步

一个更像做过线上系统的说法

如果线上出现 PHP-FPM 打满,我不会第一时间只改参数。我会先看 max_children 是否打满、slowlog 里卡的是 SQL 还是外部接口、Nginx 到 FPM 的队列有没有堆积,再决定是优化慢请求、拆异步、加缓存,还是在机器资源允许的前提下调大进程池。

这句话很加分,因为它体现的是排查顺序。


十一、FastCGI 参数和 PHP 执行上下文

这块不一定每次都会问,但问到了很加分。

当 Nginx 把请求发给 PHP-FPM 时,除了把请求体发过去,还会带一组 FastCGI 参数,比如:

  • SCRIPT_FILENAME
  • REQUEST_METHOD
  • QUERY_STRING
  • CONTENT_TYPE
  • CONTENT_LENGTH

PHP-FPM 的 worker 会基于这些参数构造 PHP 运行环境,例如:

  • $_SERVER
  • $_GET
  • $_POST

所以你可以理解成:

Nginx 负责把 HTTP 请求转换成 FastCGI 环境,PHP-FPM 再基于这些环境去执行 PHP 脚本。


十二、为什么静态资源一般不走 PHP-FPM

因为静态资源不需要 PHP 解释执行。

如果图片、CSS、JS 也都打到 PHP-FPM,会浪费 worker。

正确分工通常是:

  • 静态资源:Nginx 直接处理
  • 动态 PHP:交给 PHP-FPM

这也是为什么 Nginx + PHP-FPM 这套组合能比较高效。


十三、项目里怎么讲更像你做过真实系统

你可以这样说:

我对 PHP-FPM 的理解不只是“它是 PHP 进程池”。更重要的是,我会把它放回真实请求链路里看:Nginx 负责接入和静态资源,动态请求通过 FastCGI 转给 PHP-FPM,PHP-FPM 再把请求交给 worker 执行。线上如果 FPM 打满,我会先看 worker 是否耗尽、slowlog 卡在哪一层、listen queue 有没有堆积,再决定是优化慢 SQL、处理外部依赖超时,还是扩进程池。

这就把“原理”和“项目经验”连起来了。


十四、30 秒版怎么答

FastCGI 是 Web Server 和外部程序通信的一种协议,解决的是 CGI 每个请求都临时起进程的性能问题。PHP-FPM 是 PHP 的 FastCGI 进程管理器,负责维护一组常驻的 PHP worker。Nginx 收到动态请求后,会通过 FastCGI 把请求转给 PHP-FPM,再由空闲 worker 执行脚本并返回结果。


十五、1 分钟版怎么答

我会把 CGI、FastCGI 和 PHP-FPM 分开讲。CGI 是早期动态请求模型,每个请求都起一个外部进程,开销很大;FastCGI 是它的改良版,通过常驻进程和协议通信避免反复起进程;PHP-FPM 则是 PHP 在 FastCGI 模式下的进程管理器,负责维护 master 和一组 worker。Nginx 负责连接接入、静态资源和反向代理,动态请求通过 FastCGI 协议交给 PHP-FPM,再由 worker 执行 PHP 脚本。线上如果 PHP-FPM 打满,我会先看 slowlog、worker 数、listen queue 和下游依赖,而不是只会调大 max_children。


十六、3 分钟版怎么答

我理解这题的关键,是把 CGI、FastCGI 和 PHP-FPM 的边界讲清楚。CGI 是早期 Web Server 调外部程序的方式,但它的问题是每个请求都要临时创建进程,高并发下成本太高。FastCGI 本质上是 CGI 的常驻进程化版本,它通过协议让 Web Server 和常驻进程通信,避免重复 fork/exec。

PHP-FPM 不是协议本身,而是 PHP 的 FastCGI 进程管理器。它内部通常有 master 和 worker 两类进程,master 负责监听 socket、拉起和回收 worker、平滑重载配置,worker 负责真正执行 PHP 脚本,而且一个 worker 同一时刻通常只处理一个请求。

所以实际链路通常是:客户端把 HTTP 请求打到 Nginx,Nginx 判断静态资源还是动态请求;静态资源直接由 Nginx 处理,动态请求通过 FastCGI 转发给 PHP-FPM;PHP-FPM 从进程池里选择空闲 worker 执行脚本,执行完成后再把结果返回给 Nginx。

线上排查时,我更关注的是 worker 是否耗尽、slowlog 卡在 SQL 还是外部依赖、listen queue 有没有堆积。因为 FPM 打满通常不是单纯参数小,而是请求处理速度跟不上请求到达速度。调大 max_children 只是手段,真正要看是不是慢 SQL、缓存失效、第三方接口超时或者突发流量带来的问题。


十七、这题最容易答错的地方

1. 把 FastCGI 和 PHP-FPM 当成一个东西

更准确的说法应该是:

  • FastCGI 是协议
  • PHP-FPM 是 FastCGI 进程管理器

2. 只会背 Nginx + PHP-FPM

面试官更想听的是:

  • 为什么需要它们分工
  • 请求是怎么流转的
  • 为什么会打满

3. 遇到问题只会说“调大参数”

真正更好的说法是:

  • 先分析慢在哪里
  • 再决定扩容还是优化

十八、你可以记住的最终一句话

FastCGI 解决的是 CGI 每次都起进程的问题,PHP-FPM 解决的是 PHP 在 FastCGI 模式下怎么稳定地管理一组常驻 worker,Nginx 则负责接入、转发和静态资源处理。