frankenphp-worker 必须显式指定 --script 绝对路径和 --addr,否则启动失败;worker.php 需分初始化、请求循环、清理三层,且环境变量须预注入,不可依赖 .env 动态加载。

frankenphp-worker 命令必须显式指定 --script
FrankenPHP 独立二进制不自动启用 Worker 模式,哪怕你用了 frankenphp-worker 这个命令名。它默认只是个“可执行入口”,真正开启 worker 的关键在参数:--script 必须带一个**绝对路径**指向你的 worker 入口文件(如 /app/public/worker.php)。漏掉这个参数,进程会直接退出并报错 flag needs an argument: --script。
常见错误是写相对路径:--script public/worker.php —— 在独立二进制中当前工作目录不可靠,路径解析失败会导致启动失败;或误用 frankenphp php-server --worker(这是旧版 Caddy 集成写法,不适用于纯二进制)。
- 正确写法:
./frankenphp-worker --script /app/public/worker.php --addr :8080 -
--addr必须显式指定,否则监听默认端口可能被占或行为不确定 - 确保该脚本文件存在、可读,且 PHP 解释器能加载其依赖(如
vendor/autoload.php)
worker.php 必须用 frankenphp_handle_request 包裹请求逻辑
独立二进制的 Worker 模式不会自动重置超全局变量或清理框架状态。如果你沿用 public/index.php 直接当 worker 入口,首次请求后大概率出现 Class not found 或数据库连接复用异常——因为 autoloader 和连接对象被长期持有,但未做隔离。
正确的 worker.php 结构要分三层:初始化(一次)、请求循环(多次)、清理(每次):
- 循环外:调用
require_once __DIR__.'/../vendor/autoload.php'和$kernel = new Kernel($_ENV['APP_ENV'], (bool) $_ENV['APP_DEBUG']),再执行$kernel->boot() - 循环内:用
frankenphp_handle_request(function () use ($kernel) { ... })包裹请求处理,里面构造$request、调用$kernel->handle($request)、$response->send() - 每次请求后:手动调用
gc_collect_cycles(),重置$_SERVER['REQUEST_URI']等(FrankenPHP 不保证完全重置)
环境变量和配置需提前注入,不能依赖 .env 文件动态加载
独立二进制打包后,.env 文件通常不随二进制分发(除非你显式嵌入),而 Dotenv::createUnsafeImmutable() 在常驻进程中只生效一次。如果 worker 启动时没读到 APP_ENV,Kernel 会按默认值初始化,后续无法更改。
解决方案是启动前通过系统环境变量传入,而非让 PHP 自己去读文件:
- Linux 下:
APP_ENV=prod APP_DEBUG=0 ./frankenphp-worker --script /app/public/worker.php - Docker 中:用
-e APP_ENV=prod -e APP_DEBUG=0透传 - 避免在
worker.php开头写if (file_exists(__DIR__.'/.env')) { (new Dotenv())->load(__DIR__.'/.env'); }—— 第二次请求时该逻辑不会重执行,变量就卡死了
启动后验证是否真在 Worker 模式运行
独立二进制没有内置 metrics 端点(不像 Caddy 集成版可通过 /metrics 查 frankenphp_busy_threads),所以得靠外部观察确认是否生效:
- 用
ps aux | grep frankenphp查进程数:正常应只有 1 个主进程,而不是每请求一个子进程 - 发两个连续请求,用
curl -v http://localhost:8080观察响应头中的X-Powered-By或自定义 header,若两次返回一致(比如都含X-Boot-Time: 1728796200),说明$kernel->boot()只执行了一次 - 故意在
worker.php循环外加error_log('booted at '.time());,看日志是否只输出一行 —— 多行即说明被重复加载,Worker 没跑起来
最容易被忽略的是:独立二进制的 frankenphp-worker 不会自动处理信号(如 SIGTERM),你要在代码里注册 pcntl_signal(SIGTERM, fn() => exit(0)) 并调用 pcntl_signal_dispatch(),否则 kill -15 无法优雅停止。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











