FrankenPHP Worker重启会中断请求,因其无平滑重启机制,所谓“不中断”实为误判;真正零停机需靠入口动态加载+框架状态重置,而非进程重启。

FrankenPHP Worker重启时不会中断正在处理的请求
FrankenPHP 的 Worker 模式本身不提供“主动重启 Worker 进程”的机制——它没有类似 Swoole 的 max_request 或 Workerman 的 reload 信号模型。所谓“重启 Worker”,实际是指整个 FrankenPHP 进程(即主进程)被终止后重新启动,此时所有正在运行的请求都会被强制中断。
你看到的“请求没断”现象,往往来自两个误判:
- 你以为在 reload,其实只是浏览器重试或前端自动轮询(比如 AJAX 502 后自动发新请求)
- 你用的是反向代理(如 Caddy/Nginx)+ FrankenPHP 组合,代理层做了连接复用或缓存,掩盖了后端中断
FrankenPHP 没有平滑重启能力,必须靠外部进程管理
FrankenPHP 1.1 不监听 SIGUSR1、SIGTERM 等信号来触发 worker 逐个退出;它只响应 SIGINT(Ctrl+C)和 SIGTERM 做整体退出。这意味着:
- 发送
kill -TERM $(pidof frankenphp)→ 主进程立即停止接受新请求,并等待当前frankenphp_handle_request()回调执行完毕后退出 - 但「等待」仅限于当前正在执行的那个回调,不包括已进入 PHP 用户态但尚未返回的协程、未完成的 cURL 异步请求、或阻塞在
fread()上的流操作 - 没有“拒绝新连接但继续服务老连接”的中间状态,也没有 worker 进程池概念——整个进程就是唯一的“worker 容器”
真正能保住请求的唯一办法:不重启,只热更新逻辑
如果你的目标是上线新代码又不让请求失败,不要指望重启 FrankenPHP 进程,而应聚焦在入口文件内部做动态加载:
立即学习“PHP免费学习笔记(深入)”;
- 把路由、控制器、中间件等业务逻辑放在
frankenphp_handle_request()回调内,用require或include动态引入(而非require_once) - 确保框架未启用 OPcache 缓存这些文件(CLI 模式下
opcache.enable_cli=1会导致require失效) - 避免在回调外初始化单例、静态变量或长连接资源(如 PDO 实例),否则 reload 后仍复用旧状态
- 若用了 Laravel,可在每次请求前调用
$app->forgetInstance('router')和$app->resetResolvedInstances(),强制重建绑定
别把 FrankenPHP 当成 Swoole 或 Workerman 用
FrankenPHP 的 Worker 模式本质是“常驻 PHP 解释器 + 手动循环”,不是多进程/多线程服务器。它的稳定性依赖你写的入口逻辑是否干净,而不是框架或运行时帮你兜底。最容易被忽略的一点是:frankenphp_handle_request() 回调里抛出未捕获异常,会直接终止整个进程——这比重启更致命,因为连 graceful shutdown 都没机会执行。



















