FrankenPHP仅托管HTTP请求生命周期,不接管CLI进程,因此队列(queue:work)和定时任务(schedule:work)必须独立运行并由supervisor等工具守护。

FrankenPHP 容器里 Laravel 的队列和定时任务不能靠 FrankenPHP 本身托管,必须独立运行、单独守护。它只管 HTTP 请求生命周期,不接管 CLI 进程;queue:work 和 schedule:work 是纯命令行任务,跟 Web 服务器进程完全隔离。
为什么 FrankenPHP 不处理队列和定时任务
FrankenPHP 的职责边界非常清晰:它是一个嵌入 PHP 运行时的 HTTP 服务器(基于 Caddy),只响应 TCP/HTTP 请求。所有 php artisan 命令都走 CLI SAPI,不经过 FrankenPHP 的请求路由或 worker 生命周期管理。即使你开了 worker 模式,那也只是针对 HTTP 入口(index.php)的常驻,不是让整个 Laravel 应用“全局常驻”。
常见误解是以为 “FrankenPHP 启动了,Laravel 就全常驻了”——其实只是框架初始化部分被复用,而 queue:work、schedule:work、自定义命令这些仍需另起进程,且必须由外部进程管理工具维持。
在 FrankenPHP 容器中启动并守护 queue:work
容器内不能依赖系统级 supervisord 默认服务(很多 FrankenPHP 镜像精简掉了),推荐用 supervisord 显式启动,或改用更轻量的 s6-overlay。但最直接可控的方式是:在容器启动时用 supervisord 同时拉起 FrankenPHP Web 服务 + 队列工作进程。
立即学习“PHP免费学习笔记(深入)”;
- 确保你的 Docker 镜像已安装
supervisor(如dunglas/frankenphp基础镜像不含,需自定义构建) - 配置
/etc/supervisor/conf.d/laravel-queue.conf:
[program:laravel-queue] command=php /app/artisan queue:work --sleep=3 --tries=3 directory=/app autostart=true autorestart=true user=www-data stopasgroup=true killasgroup=true
- 关键点:
stopasgroup和killasgroup能避免子进程残留;--sleep=3防止空轮询压 CPU;--tries=3控制失败重试次数 - 不要用
--daemon:该参数已被 Laravel 9+ 弃用,且与 supervisor 冲突;只用queue:work默认行为即可
在 FrankenPHP 容器中运行 schedule:work 替代系统 Cron
Docker 容器默认无 cron 守护进程,而 schedule:run 依赖外部 cron 每分钟调用一次,不可靠也不符合容器设计原则。正确做法是用 schedule:work 命令长期运行,由 supervisor 管理其生命周期。
- 添加另一段 supervisor 配置(如
/etc/supervisor/conf.d/laravel-schedule.conf):
[program:laravel-schedule] command=php /app/artisan schedule:work directory=/app autostart=true autorestart=true user=www-data redirect_stderr=true stdout_logfile=/app/storage/logs/schedule-work.log
-
schedule:work是 Laravel 8.12+ 引入的替代方案,它内部用毫秒级循环监听任务,精度远高于 cron 的 1 分钟粒度 - 日志路径必须可写(
/app/storage/logs/),否则 supervisor 会反复重启该进程 - 注意时区:确保容器内
date输出的时区与config/app.php中的'timezone'一致,否则任务可能错时执行
多个 CLI 进程共存时的资源与信号陷阱
FrankenPHP 容器里同时跑 Web 服务 + 队列 + 调度,本质是多个 PHP CLI 进程共享一个容器环境,容易踩三个坑:
-
memory_limit冲突:CLI 和 Web 的php.ini可能不同。检查php -i | grep memory_limit和php -r "echo ini_get('memory_limit');"是否一致;建议统一设为512M或更高 - 信号干扰:FrankenPHP 主进程接收
SIGTERM会优雅退出,但 supervisor 管理的子进程若没正确处理信号,可能变成僵尸进程。务必在queue:work和schedule:work运行期间禁用pcntl_async_signals(true)类扩展(FrankenPHP 官方镜像默认关闭) - 数据库连接泄漏:Laravel 队列进程长期运行后,PDO 连接可能超时断开。必须在
App\Providers\AppServiceProvider::boot()中注册DB::reconnect()或使用DB::purge()清理连接池
真正难的不是启动,而是让这三个进程在同一个容器里互不干扰地活过一周——重点盯日志、限制内存、显式关闭非必要扩展,比堆参数更重要。



















