答案是:不一定。这取决于你使用的 PHP 运行模式(SAPI)。
- 在传统的 PHP-FPM (Nginx/Apache) 模式下:是的,通常是两个独立的进程。
- 在 Hyperf/Swoole (常驻内存) 模式下:不是,通常是同一个进程内的两个协程 (Coroutines) 或线程。
如果把服务器比作一家餐厅:
- PHP-FPM:是快餐店模式。
- 机制:每个顾客(请求)来了,经理就叫一个空闲的厨师(Worker 进程)专门服务他。服务完,厨师休息或下班。
- 结果:两个顾客 = 两个厨师(两个进程)。他们互不干扰,隔离性极好,但切换成本高。
- Hyperf/Swoole:是高端居酒屋/寿司吧台模式。
- 机制:只有一个或几个大厨(Worker 进程)坐在吧台后。
- 结果:两个顾客(请求)来了,大厨左手给 A 捏寿司,右手给 B 倒酒(协程切换)。
- 本质:两个顾客由同一个厨师(进程)通过快速切换注意力(协程)来服务。
- 核心逻辑:别以为多个人吃饭就需要多个厨房。高手可以用一只手同时伺候多桌客人,只要他切换得够快。
一、传统模式:PHP-FPM (Multi-Process)
1. 机制:Pre-fork 模型
- 启动时:Nginx 启动一组 PHP-FPM Worker 进程(如 5 个)。
- 请求到来:
- 浏览器 A 请求 -> Nginx 转发给 FPM -> FPM 管理器分配Worker Process 1。
- 浏览器 B 请求 -> Nginx 转发给 FPM -> FPM 管理器分配Worker Process 2(如果 Process 1 忙)。
- 执行:
- Process 1 加载 PHP 代码,初始化环境,执行业务逻辑,返回结果,然后重置状态等待下一个请求。
- Process 2 同理。
- 结论:是两个完全独立的操作系统进程。
- 内存隔离:Process 1 的变量不会影响 Process 2。
- PID 不同:在服务器上
ps aux | grep php-fpm能看到两个不同的 PID。
2. 特点
- 优点:稳定性高。一个进程崩溃(Segfault)不影响其他进程。
- 缺点:资源消耗大。每个进程都要加载完整的框架、配置文件、类定义。并发高时,进程上下文切换开销大。
二、现代模式:Hyperf/Swoole (Multi-Coroutine / Multi-Thread)
1. 机制:Event Loop + Coroutine
- 启动时:Hyper 启动 4 个 Worker 进程(假设配置
worker_num=4)。 - 请求到来:
- 浏览器 A 请求 -> Swoole Event Loop 接收 -> 在Worker Process 1中创建Coroutine A。
- 浏览器 B 请求 -> Swoole Event Loop 接收 -> 在Worker Process 1中创建Coroutine B(或者分配到 Worker Process 2,取决于负载均衡)。
- 执行:
- Coroutine A 执行到 IO 操作(查数据库),挂起 (Yield),让出 CPU。
- Coroutine B 获得 CPU 时间片,开始执行。
- 数据库返回结果,Coroutine A恢复 (Resume),继续执行。
- 结论:通常是在同一个进程内,由两个协程并发处理。
- 内存共享:Coroutine A 和 B 共享同一个进程的内存空间(全局变量、静态属性)。
- PID 相同:在服务器上
ps aux | grep hyperf可能只看到一个 Worker 进程在处理这两个请求。
2. 特点
- 优点:极高并发。无需创建/销毁进程,内存复用,上下文切换极快(用户态切换)。
- 缺点:开发门槛高。必须注意协程安全,避免在协程间共享可变状态导致数据污染。
三、特殊情况:多线程 (ZTS / Apache Worker MPM)
- Apache Worker MPM或PHP ZTS (Zend Thread Safety)模式:
- 一个进程内包含多个线程 (Threads)。
- 两个请求可能由同一个进程内的两个不同线程处理。
- 结论:是一个进程,两个线程。
- 现状:这种模式在现代 PHP Web 开发中已较少见,主流是 FPM(多进程)或 Swoole/OpenSwoole(多进程+协程)。
四、认知牢笼:常见误区
1. 误区:“浏览器开了两个窗口,服务器就必须开两个进程。”
- 真相:服务器如何处理请求,取决于服务器架构,而非客户端行为。
- 对策:理解服务器的并发模型。FPM 是“一人一事”,Swoole 是“一人多事”。
2. 误区:“Swoole 下,两个请求绝对在同一个进程。”
- 真相:不一定。
- 如果配置了
worker_num=4,且有 4 个空闲 Worker,Nginx/Swoole 可能会将请求轮询分发到不同的 Worker 进程。 - 但如果只有 1 个 Worker,或者该 Worker 很忙,它们可能在同一进程。
- 关键:即使在不同进程,它们也是协程而非传统意义上的“进程阻塞”。
- 如果配置了
3. 误区:“进程比协程安全,所以 FPM 更好。”
- 真相:
- FPM:进程隔离带来安全性,但牺牲了性能。
- Swoole:协程共享内存带来高性能,但要求开发者具备线程/协程安全意识(如无状态设计、Context 管理)。
- 对策:根据项目规模选择。小项目 FPM 足够;高并发微服务选 Swoole/Hyperf。
4. 误区:“我可以利用‘两个进程’来通信。”
- FPM:进程间通信 (IPC) 很麻烦(Redis, Socket, Shared Memory)。
- Swoole:同进程内协程通信很简单(Channel, Context);跨进程通信需用 Table, Redis, MQ。
- 对策:不要依赖进程ID来做业务逻辑。使用分布式 ID 或会话存储。
🚀 总结:原子化“浏览器窗口 vs. PHP 进程”全景图
| 维度 | PHP-FPM (传统) | Hyperf/Swoole (现代) |
|---|---|---|
| 并发模型 | 多进程 (Multi-Process) | 多进程 + 多协程 (Multi-Coroutine) |
| 两个请求 | 通常对应两个独立进程 | 通常对应同一进程内的两个协程 |
| 内存隔离 | 完全隔离(进程级) | 部分共享(进程内协程共享内存) |
| 资源开销 | 高(每次加载框架) | 低(内存常驻,复用对象) |
| 安全性 | 高(崩溃不影响其他) | 中(需防止协程间数据污染) |
| PHP 隐喻 | Multiple Chefs vs. One Chef with Two Hands | |
| 公式 | Requests = Processes (FPM); Requests = Coroutines (Swoole) |
终极心法:
浏览器窗口的数量,不等于服务器进程的数量。
FPM 用空间换隔离,Swoole 用复杂度换性能。
理解你的战场,才能选择正确的武器。
于进程中见隔离,于协程见并发;以模型为尺,解混淆之牛,于架构选型中,求适配之真。
行动指令:
- 检查环境:确认你的项目是跑在 FPM 还是 Hyperf/Swoole 上。
- 观察进程:
- FPM:
ps aux | grep php-fpm,发起两个请求,看是否新增或占用不同 PID。 - Hyperf:
ps aux | grep hyperf,发起两个请求,观察 Worker 进程的 CPU 变化,PID 通常不变。
- FPM:
- 思维升级:记住,在 Hyperf 中,你要担心的不是“进程冲突”,而是“协程上下文污染”。确保你的代码是无状态的,或者正确使用
Context。