FrankenPHP中长连接本身不消耗PHP Worker资源,仅Go层管理连接并承担CPU/内存开销;PHP Worker仅在有实际请求时调度,空闲长连接期间保持闲置。

FrankenPHP 本身不维护长连接的 PHP 执行上下文,大量长连接几乎不消耗 PHP 线程或 Worker 资源——真正吃 CPU 和内存的是 Go 层的 HTTP 连接管理,而非 PHP 层。
长连接由 Caddy 的 Go HTTP/2 或 HTTP/3 栈承载
FrankenPHP 内嵌 Caddy,所有客户端连接(包括 keep-alive、SSE、HTTP/2 流、Vulcain/Mercure 订阅)均由 Go 的 net/http 或 quic-go 处理。PHP Worker 只在有实际请求需要执行时才被调度,空闲长连接期间 Worker 完全不参与。
- 一个维持 1000 个 idle HTTP/2 连接的 FrankenPHP 实例,
frankenphp_threads_busy可能长期为 0,frankenphp_queue_depth也保持 0 - CPU 开销主要来自 Go 的 epoll/kqueue 循环和 TLS 握手/续订(尤其 OCSP Stapling 启用时),不是 PHP 解释器
- 内存占用增长集中在 Go 的连接结构体(每个连接约 4–8 KB),而非 PHP 的
memory_limit范围内
真正触发资源消耗的是“连接上有活跃请求”
长连接本身轻量,但一旦它发起新请求(比如 HTTP/2 流复用、SSE 事件推送、Mercure 广播回调),就会立即占用一个 PHP Worker。此时瓶颈回归到 workers 配置和线程池调度能力。
- 配置
workers: 4时,最多同时处理 4 个 PHP 请求,哪怕背后有 10000 个长连接 - 第 5 个并发请求会排队,表现为
frankenphp_queue_depth上升,而 CPU 可能仍很低(因等待的是锁或 I/O,不是计算) - 若长连接频繁触发小请求(如心跳 polling),会导致 Worker 频繁上下文切换,反而比稳定长连接更伤性能
如何验证长连接是否真造成压力
别看 top 的 CPU%,重点查三类指标:
立即学习“PHP免费学习笔记(深入)”;
- Go 层:用
htop -p $(pgrep -f frankenphp)观察线程状态 —— 大量S(sleep)是健康信号;若出现大量R(running)且 CPU 持续 >70%,说明 Go 的 event loop 过载(常见于 TLS 验证或证书链解析) - PHP 层:
curl http://localhost:2019/metrics | grep -E "frankenphp_threads_busy|frankenphp_queue_depth"—— 两者都低,说明长连接没带来 PHP 负担 - 系统层:
ss -s查 ESTAB 连接数,再对比ps -o pid,rss,comm -C frankenphp的 RSS —— 若连接数翻倍但 RSS 增长
容易被忽略的一点:HTTP/2 的 SETTINGS 帧、PING 帧、流优先级更新等控制流量,全由 Go 处理,不进 PHP。这些帧频率过高(如客户端误配)会抬高 Go 线程 CPU,但不会反映在任何 PHP 监控指标里——得用 perf record -p $(pgrep frankenphp) 抓热点函数才能定位。



















