frankenphp worker模式下reload不会丢请求,但存在极短“不可用窗口”:旧线程 graceful shutdown 完成前处理完当前请求,新线程池启动后接管新请求;若配置不当或资源不足,caddy可能返回502或排队延迟。

FrankenPHP Worker模式下进程重启是否丢请求
不会丢请求,但存在极短时间的“不可用窗口”,具体取决于重启方式和配置。FrankenPHP 的 Worker 模式本身不提供零停机热重载,它依赖的是进程级平滑重启(graceful restart),而非代码热更新。
关键点在于:FrankenPHP 启动时会预启一批 PHP 线程,每个线程加载一次框架后长期驻留;当执行 frankenphp reload 或收到 SIGHUP 时,它会启动新线程池、等待旧线程处理完当前请求后再退出——这叫 graceful shutdown,不是 kill -9 式硬终止。
- 旧线程不会被强制中断:只要请求没结束,
request()->fullUrl()、数据库连接、session 等上下文仍有效,响应能正常发出 - 新请求会被路由到新线程池:reload 完成后,Caddy 路由层自动切流,新进请求不再发往旧线程
- “窗口期”只存在于 reload 执行中:若旧线程池正满载,而新线程池尚未 ready,Caddy 可能短暂返回 502 或排队延迟(取决于
frankenphp_queue_depth和 Caddy 的 timeout 设置) - 没有类似 Swoole 的
octane:reload命令:FrankenPHP 的 reload 是通过 admin API 触发的,需确保 Caddy admin 端口(默认:2019)可访问且未被防火墙拦截
为什么有时 reload 后还是看到 502 或超时
这不是 FrankenPHP 本身丢请求,而是 Caddy 层或客户端侧的连锁反应。常见真实原因:
- Caddy 的
reverse_proxy超时太短:默认timeout 30s,但若旧线程正在处理一个长耗时请求(如导出大文件),reload 期间它仍在跑,新请求却因等待太久被 Caddy 主动断开 - admin API 调用失败:执行
curl -X POST http://localhost:2019/load返回 404 或 500,说明 Caddy admin 未启用或路径不对(检查 Caddyfile 是否含admin :2019) - Worker 脚本启动失败:
vendor/bin/frankenphp-worker.php权限不对、PHP 版本不兼容、或bootstrap/app.php里有 fatal error,导致新线程池根本起不来,Caddy 继续转发给已标记为 “draining” 的旧线程,直到它们全部退出 - 系统资源不足:并发线程数设太高(如
frankenphp_threads 64),reload 时新旧两套线程同时存在,内存/文件描述符打满,触发内核 OOM 或 Caddy 报upstream connect error
如何验证一次 reload 是否真正平滑
别只看终端输出,要查三个地方的数据流是否连续:
- 看指标:
frankenphp_busy_threads应先缓慢下降(旧线程处理完请求),再平稳上升(新线程接手),中间不应归零或跳变剧烈;frankenphp_queue_depth若持续 > 0 且不回落,说明新线程没接上活 - 看日志:Caddy access log 中 reload 时间点前后不应出现成片 502;FrankenPHP 的
error_log不应有PHP Fatal error或Failed to start worker - 看请求链路:用
curl -v或 Postman 发连续请求,在 reload 命令执行瞬间观察响应状态码和X-Response-Time头——理想情况是所有请求都返回 200,最大延迟不超过单个请求平均耗时的 2 倍
最易被忽略的一点:FrankenPHP 的 graceful reload 依赖 PHP 线程自身配合退出。如果业务代码在 onWorkerStop 或 shutdown 函数里做了阻塞操作(比如没设 timeout 的 file_get_contents 调用外部 API),旧线程就卡住不退,新线程又不敢全量接管,整个 reload 就僵在那里——此时队列深度会越积越高,直到 Caddy 主动断连。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











