不能在FrankenPHP Worker模式下使用exit或die,否则会终止整个Worker进程导致后续请求失败;应改用return、抛异常或框架统一处理机制确保回调自然返回。

调用 exit 或 die 会让整个 Worker 进程终止
是的,exit 和 die 在 FrankenPHP Worker 模式下不是“退出当前请求”,而是直接终止当前 PHP 进程——也就是整个 Worker 实例。后续所有请求都会因无可用 Worker 而排队或失败,表现为 502 Bad Gateway 或 frankenphp_queue_depth 持续上涨。
为什么不能用 exit?它和普通 FPM 行为完全不同
在传统 PHP-FPM 中,exit 只结束本次请求生命周期,进程池会自动拉起新 worker 处理下个请求;但 FrankenPHP 的 Worker 是长期运行的单进程,没有“请求后自动重建”机制。一旦 exit 执行,PHP 解释器立即退出,Caddy 不会主动重启它(除非配置了 startup: 1 或 max_requests 触发轮换)。
- 常见触发点:
dd()、var_dump() + exit、框架中未捕获的 fatal error 后的隐式终止、某些调试工具插入的exit(1) - 后果可观察:
curl http://localhost:2019/metrics | grep frankenphp_worker_restarts_total不增长,但frankenphp_busy_threads归零且不再恢复 - 替代方案:用
return从请求回调中退出,或抛出HttpException类异常由框架统一处理
正确终止请求的唯一方式是让 frankenphp_handle_request() 回调自然返回
Worker 模式下,每个请求都由你传入 frankenphp_handle_request() 的回调函数处理。这个回调必须执行完毕并返回,FrankenPHP 才能回收线程、重置超全局变量、准备下一次调用。任何提前中断(exit、die、未捕获的 Fatal error)都会卡死该 Worker。
- 确保框架入口(如 Symfony 的
public/worker.php)里没有裸exit,所有流程最终落到return $response - 检查中间件、事件监听器、验证逻辑——它们常含
exit风格的快速失败写法,需改为抛异常或返回响应对象 - 日志中若出现
PHP Fatal error: Uncaught Error:后无后续请求日志,基本可判定是exit类行为导致 Worker 崩溃
调试时怎么安全地“中断”而不杀 Worker
开发阶段需要打断执行流时,绝对不要用 exit。可行做法包括:
立即学习“PHP免费学习笔记(深入)”;
- 用
throw new RuntimeException('debug break'),配合顶层异常处理器返回 500 响应,Worker 继续存活 - 在回调末尾加
if (getenv('APP_ENV') === 'dev') { usleep(100000); }暂停而非终止 - 使用 Xdebug 断点,它不依赖
exit,且能在回调内安全暂停 - 临时注释掉可疑代码段,而不是加
exit来“跳过”
最易被忽略的是框架封装层里的隐式 exit —— 比如某些老版本 Monolog 的 NativeMailerHandler 在发送失败时会 die,这种必须替换或 patch。



















