FrankenPHP异步任务默认关闭,必须显式启用worker模式并配置PHP代码触发协程或goroutine;其异步能力仅在frankenphp worker模式下有效,HTTP server模式下goroutine会随请求结束被回收。

FrankenPHP 安装后异步任务默认是关闭的,必须显式启用 worker 模式并配置 PHP 代码触发协程或 goroutine;它不像 Swoole 那样自带 go 或 Swoole\Coroutine\run 全局入口,而是依赖运行时模式 + 扩展调用协同工作。
确认 FrankenPHP 运行在 worker 模式
FrankenPHP 的异步能力只在 worker 模式下可用——这是它与传统 FPM/Cgi 的根本区别。如果你用的是 caddy run 或直接 frankenphp serve(即 HTTP server 模式),PHP 代码仍运行在同步请求生命周期内,goroutine 会随请求结束被强制回收,无法真正异步。
必须改用:
- 命令行启动:运行
frankenphp worker(推荐搭配 systemd 或 supervisord 管理) - Docker 启动:确保镜像为
trueasync/php-true-async:latest-frankenphp,且command是php artisan async:franken类型 - 检查日志是否含
Starting worker mode,而非Starting HTTP server
PHP 侧触发异步逻辑的两种可靠方式
FrankenPHP 不提供 go() 函数,也不能直接 new Coroutine。异步必须通过以下任一路径落地:
立即学习“PHP免费学习笔记(深入)”;
-
调用 Go 编写的 PHP 扩展:例如你用扩展生成器写了一个
async_run_background函数,内部启动goroutine并立即返回,PHP 层感知不到阻塞 -
使用
true-asyncLaravel 适配器:它把async:franken命令封装成标准 Laravel Artisan 命令,并在 worker 启动时注册事件循环,后续调用Async::run(...)才真正进入异步上下文
错误示例:go(function () { sleep(5); file_put_contents('log.txt', 'done'); }); —— 这在 FrankenPHP 中根本不存在,会报 Fatal error: Uncaught Error: Call to undefined function go()。
常见失败原因:PHP 代码没“挂住”worker 生命周期
即使启用了 worker 模式,如果 PHP 脚本执行完就退出,goroutine 也会被终止。必须让 worker 进程持续持有运行时上下文:
- Laravel 场景:必须用
php artisan async:franken启动,而不是php artisan serve;后者仍是单请求模型 - 原生 PHP 场景:worker 启动时需加载一个长期运行的 PHP 文件(如
worker.php),里面包含while (true) { ... }或事件监听循环,否则进程秒退 - 环境变量
FRANKENPHP_WORKER=1必须存在,部分扩展(如frankenphpGo API)靠它判断当前是否处于 worker 上下文
验证异步是否生效的关键信号
别只看日志有没有“started”,要观察实际行为:
- 在 Go 扩展中启动 goroutine 后,加一行
log.Printf("goroutine launched at %v", time.Now()),然后立刻从 PHP 返回;几秒后再查日志,如果时间戳晚于 PHP 函数返回时间,说明成功异步 - 用
ps aux | grep php查看进程状态:worker 模式下应有常驻的frankenphp worker进程,且其子进程(PHP worker)不随 HTTP 请求波动 - 若用
true-async,执行php artisan async:status应显示 workers 在线数、待处理任务队列长度等实时指标
最容易被忽略的一点:FrankenPHP 的 worker 模式不自动接管所有 PHP 代码——它只提供运行容器,异步逻辑仍需你主动设计生命周期和资源释放,比如 goroutine 内部的 defer 清理、channel 关闭、context 取消传递,否则可能内存泄漏或僵尸 goroutine 积压。



















