应通过/metrics端点观察frankenphp_total_threads是否上涨并趋近max_threads,且与frankenphp_busy_threads差值长期>0,压测后数值逐步回落而非瞬时归零,方可确认真扩容。

怎么看frankenphp_total_threads有没有真扩容
扩容不是靠猜,得盯住指标。FrankenPHP 的 /metrics 端点暴露了关键数据,其中 frankenphp_total_threads 是当前存活的总线程数(含初始 num_threads 和动态新增的),而 frankenphp_busy_threads 表示正在处理请求的线程数。两者差值长期 > 0,才说明扩容确实发生了。
验证步骤:
- 先用
curl -s http://localhost/metrics | grep frankenphp_total_threads记下初始值(应 ≈num_threads) - 用
ab -n 200 -c 50 http://localhost/或hey -n 200 -c 50 http://localhost/模拟短时高并发 - 压测过程中反复查指标,若
frankenphp_total_threads上涨且稳定在max_threads附近,说明扩容生效 - 压测结束后观察回落:数值应逐步下降,但不会立刻回到初始值——Go runtime 会缓存部分线程几秒再回收
为什么frankenphp_queue_depth涨了却没扩容
frankenphp_queue_depth 持续升高,但 frankenphp_total_threads 卡死不动,大概率是 max_threads 已到上限,或扩容被阻塞。常见原因有:
-
max_threads设得太低(比如和num_threads相同),根本没留扩容空间 - 系统级限制:ulimit -n 太小,新线程无法创建 socket 或打开文件描述符
- 内存不足:每个新线程至少吃 20MB+,
max_threads=32在 Laravel 下可能直接触发 OOM,系统 kill 进程前就卡住了 - Go runtime 调度器过载:
GOMAXPROCS小于可用 CPU 核数,导致新线程无法被调度
查法:dmesg -T | tail -20 看有没有 Out of memory: Kill process;cat /proc/$(pgrep -f frankenphp)/limits | grep "Max open files" 看文件描述符是否耗尽。
立即学习“PHP免费学习笔记(深入)”;
curl压测时如何确认扩容后的请求真被分发了
光看线程数涨了不够,得确认新增线程确实在干活。最直接的办法是加日志打点:
- 在 PHP 入口脚本(如
public/index.php)顶部加:error_log("REQ handled by thread: " . getmypid()); - 用
tail -f /var/log/frankenphp/error.log | grep "handled by thread"实时观察 - 压测时如果看到大量不同 PID(尤其是超出初始
num_threads范围的),说明扩容线程已参与请求处理
注意:getmypid() 返回的是 OS 线程 ID,在 FrankenPHP 中每个 PHP 线程对应一个独立 PID,不是传统 FPM 的 worker 进程 ID。
别把max_wait_time当扩容开关
max_wait_time 控制的是请求在队列里最长等多久,它**不触发扩容**,只决定“等不及就超时”。很多人误以为调大这个值就能让系统自动多开线程,其实完全相反:它会让请求在队列里憋更久,反而掩盖扩容需求。
正确姿势:
- 设一个合理值(比如
max_wait_time 100ms),让系统在排队超时前优先扩容 - 如果发现大量请求在
max_wait_time达标后才返回 503,说明扩容太慢或根本没扩——这时该调的是max_threads和系统资源,不是继续拉高max_wait_time
真正影响扩容速度的是 Go runtime 的线程创建策略和底层调度,不是 PHP 层配置。这点容易被忽略,但线上出问题时往往卡在这儿。



















