PHP 8.1 引入的 Fibers 是有完整栈、可中断的函数,每个 Fiber 拥有自己的调用栈,可在任意嵌套深度挂起。这一底层原语让 Amp、ReactPHP 等框架得以构建协作式调度,但它本身并不等于”非阻塞 I/O”——在 Fiber 内调用 sleep()、file_get_contents()、同步 PDO::query() 仍会阻塞整个 PHP 进程。因此,评判 Fibers 在 API 网关中的表现,必须先厘清两个维度:内存开销从哪来,吞吐量收益在什么条件下成立。

内存开销:栈深度决定一切
每个 Fiber 维护独立的调用栈,内存占用随栈深度线性增长。社区基准显示:1000 个并发 Fiber 若各自持有深调用栈,虚拟内存预留相当可观;1k 个 Fiber 在 PHP 8.3 下约占用 76MB,PHP 8.5 动态栈优化后降至约 45MB。
⚠️ 关键认知:Fiber 的栈是”按需增长”的,但上下文切换本身仍有微秒级开销。无节制地
new Fiber()而不加连接池、超时与并发上限控制,内存会迅速膨胀。
在 API 网关场景中,内存的主要消耗点并非 Fiber 对象本身,而是:
-
每个请求持有的 DB 连接、HTTP 客户端句柄
-
调用栈深度(中间件链越长,单个 Fiber 栈越大)
-
全局缓存未设 TTL 导致的堆积
吞吐量:I/O 密集型才有显著收益
公开压测数据(Ubuntu 24.04、i7-12700K、OPcache+JIT)显示,在轻量 JSON 回显场景下:
|
方案 |
QPS |
内存/请求 |
|---|---|---|
|
传统 PHP-FPM |
~1,200 |
8–12 MB |
|
PHP 8.5.7 原生协程(Fiber + 事件循环) |
~9,800 |
1–2 MB |
|
Swoole 5.0 协程 |
25,000–30,000 |
单进程 ~100MB/千并发 |
吞吐量提升的本质不是 Fiber 魔法,而是消除了两件事:FPM 的进程创建/销毁开销、每次请求的数据库连接重建开销。Swoole 因在 C 层完成协程调度并提供完整的协程化客户端、连接池、Channel 等工业级组件,在极限 QPS 上仍领先纯 Fiber 方案。
Fiber 路线的真实定位是:作为 Zend VM 级别的调度原语,配合 Revolt 事件循环,将单进程并发能力从几百级拉到万级,但所有网络 I/O 必须走协程化驱动(amphp/mysql、revolt/event-loop 等)。
API 网关落地的四个硬约束
1. 阻塞调用零容忍
sleep()、file_get_contents()、同步 PDO::query() 会拖垮整个事件循环。必须替换为协程化客户端。
2. 并发数必须有上限
1000 个并发 HTTP 请求无限制 fan-out,可能瞬间打满内存或压垮下游。网关层应加 Worker 数限制与信号量。
3. 全局状态是隐形雷区
Fiber 之间共享进程内存,静态属性、全局变量的读写在挂起点会产生竞态。网关的中间件上下文必须显式传递,禁用 static 缓存。
4. 异常必须逐 Fiber 捕获
任何一个 Fiber 抛出未捕获异常,都会传导到 ->start() 或 ->resume() 的调用点;若调度器未做 try/catch 隔离,整个事件循环会崩溃。
基准测试该怎么跑才客观
若要在团队内做 Fibers 网关的基准对比,建议锁定以下变量:
-
硬件:4 核 8GB 起步,记录 CPU 型号与内存
-
PHP 版本:8.1+,开启 OPcache + JIT
-
场景:
/ping轻量回显、单 DB 查询、5 路外部 API 扇出 -
工具:
hey或wrk,并发 1000、持续 60s -
对照组:PHP-FPM + cURL、Fiber + Revolt、Swoole 5.x
-
观测指标:QPS、P99 延迟、内存峰值、FD 数
只有固定上述条件,得出的 QPS 与内存数据才有横向可比性。脱离测试环境的百分比提升都是营销话术。
结论很朴素:Fibers 给 PHP 带来了真正的协作式并发原语,内存开销可控、I/O 密集型网关场景吞吐量可获数倍提升;但它不是”Swoole 替代品”,更不是”加了就快”的银弹。在 API 网关这种高并发、长调用链、强依赖外部服务的场景下,Fiber + Revolt 事件循环 + 协程化客户端 + 连接池才是可落地的组合,而内存与吞吐的基准数字,必须由团队在自己的硬件与业务画像下重新跑一遍。

