frankenphp_total_threads 指标值应严格等于 Caddyfile 中 frankenphp { num_threads N } 配置的 N 值,且需与启动日志“frankenphp: starting with N threads”及 metrics 端点输出一致,三者不一致说明配置未生效。

检查 frankenphp_total_threads 指标是否匹配预期
FrankenPHP 的线程数不是靠 ps 或 top 看进程数判断的——它用的是固定数量的 Go goroutine 绑定 PHP 线程,不产生独立系统进程。真正生效与否,得看指标值是否和你配置的 num_threads 一致。
先确认 Caddy metrics 已启用(Caddyfile 里有 metrics),然后执行:
curl http://localhost:2019/metrics | grep frankenphp_total_threads
输出类似 frankenphp_total_threads 8 才算对。如果仍是默认值(比如 4 或 16),说明配置没加载成功。
- 常见错误:把
frankenphp { num_threads 8 }写在了route块里,而不是全局{}块或php指令块内——FrankenPHP 配置必须在frankenphp指令作用域下才生效 - 另一个坑:改完 Caddyfile 后只
caddy reload,但某些旧版本 FrankenPHP 的线程池不会热更新,必须caddy stop && caddy start - 如果你用了 Docker,注意环境变量覆盖:镜像
dunglas/frankenphp会读FRANKENPHP_NUM_THREADS,它优先级高于 Caddyfile 里的num_threads
对比 frankenphp_busy_threads 和实际负载
光看总数不够,得验证线程是否真被调度使用。压测时持续抓指标:
立即学习“PHP免费学习笔记(深入)”;
watch -n 1 'curl -s http://localhost:2019/metrics | grep -E "frankenphp_(total|busy)_threads"'
观察两者的差值:frankenphp_total_threads - frankenphp_busy_threads 应该随请求量动态变化。如果 busy 一直卡在 0 或极低值,说明请求根本没进 PHP 线程池——大概率是路由没匹配到 php 指令,或者静态文件拦截提前返回了(比如 try_files 把 index.php 当作文件返回了)。
- 典型场景:Laravel 项目漏配
try_files {path} /index.php?{query},导致所有请求 404,自然没有 PHP 线程被占用 - Worker 脚本会恒定占用线程:只要启了
worker { file ... },frankenphp_busy_threads至少等于worker.num,哪怕没流量 -
frankenphp_queue_depth> 0 且持续增长,说明线程数确实不够,请求开始排队
查日志确认线程池初始化日志是否出现
Caddy 启动时会在 stdout 或 caddy.log 里打印 FrankenPHP 初始化信息,关键行是:
frankenphp: starting with X threads (max Y)
这个 X 就是最终生效的 num_threads。如果日志里没这行,或者数字和你配的不符,基本可断定配置未被识别。
- 常见原因:Caddyfile 语法错误导致
frankenphp块被跳过(比如漏了}或缩进错乱),Caddy 会静默忽略非法块 - Docker 场景下,检查容器启动日志:
docker logs <container>,别只看docker ps是否运行中 - 如果用
max_threads auto,日志会显示类似max auto (based on 8 cores),但total_threads指标仍以num_threads为准,auto只影响扩容上限
为什么 ps aux | grep php 看不到多个 PHP 进程
这是最常让人困惑的点:FrankenPHP 不是 PHP-FPM,它把 PHP 运行时嵌在 Caddy 进程里,用 Go 的 goroutine 管理 PHP 线程,所以 ps 只能看到一个 caddy 进程。试图通过进程数反推线程数,逻辑就错了。
正确思路是:线程数 = 指标值 = 配置值 = 日志值。三者不一致,说明某处配置或加载失败;三者一致但性能没提升,问题大概率出在 PHP 代码层(比如没启用 OPcache、框架 bootstrap 太重),而不是线程数本身。
尤其注意 max_wait_time:它控制请求排队超时,默认禁用。如果设了 10s 却没看到 frankenphp_queue_depth 上升,可能是请求太快被消化了,也可能是该参数根本没生效(仅当 num_threads 显式配置时才有效)。



















