FrankenPHP 不接管 Messenger 消费进程,messenger:consume 必须由 Supervisor 或 systemd 独立管理;需用绝对路径、统一用户、启用 autorestart 和 stopwaitsecs,并确保环境变量与传输层配置(如 DSN)与 Web 请求一致。

FrankenPHP 本身不接管 Messenger 消费进程
FrankenPHP 只负责 HTTP 请求的常驻处理(包括 worker 模式下的 PHP 应用生命周期复用),但它不会自动启动、管理或守护 Symfony 的 messenger:consume 这类 CLI 消费进程。这些进程仍需你手动部署为独立常驻服务,和 Nginx+PHP-FPM 场景下完全一致 —— FrankenPHP 并没改变 Messenger 的运行模型。
必须用 Supervisor 或 systemd 管理 messenger:consume
推荐用 Supervisor,它和 FrankenPHP 部署场景天然兼容(都是 Linux 服务化思路)。关键点不是“怎么配 FrankenPHP”,而是“怎么让消费进程不死”:
-
command必须用绝对路径:比如/usr/bin/php /var/www/myapp/bin/console messenger:consume async --time-limit=3600 -
user要和 FrankenPHP 运行用户一致(如www-data),避免文件权限冲突 -
autorestart=true和startretries=3是底线配置,否则异常退出就卡住 -
stopwaitsecs=120很重要:给 Messenger 留足时间完成当前消息再退出,防止任务中断 - 日志路径单独指定,别和 FrankenPHP 的 access.log 混在一起
别在 FrankenPHP 的 worker 模式里塞 messenger:consume
有人试图把 messenger:consume 塞进 FrankenPHP 的 worker.php 或通过 php-fpm 风格的循环启动——这会出问题:
- FrankenPHP 的 worker 模式只对 HTTP 请求生命周期有效,
messenger:consume是长时 CLI 进程,两者调度逻辑冲突 - PHP 内存模型不同:HTTP worker 有请求边界和自动 GC 触发点;CLI 消费进程靠自己控制内存释放,混在一起容易累积泄漏
- FrankenPHP 不提供信号转发机制,
supervisorctl stop发的 SIGTERM 无法可靠传给嵌套的消费进程
环境变量和传输层要和 Web 请求保持一致
FrankenPHP 启动的 Web 请求和 Supervisor 启动的消费进程,必须共享同一套环境配置,否则会出现“Web 端发消息成功,消费端连不上 Redis”的情况:
立即学习“PHP免费学习笔记(深入)”;
- 确保
.env文件被两个进程同时读取(不要在 Supervisor 的environment里重复写 DSN) - 检查
MESSENGER_TRANSPORT_DSN是否指向同一个 Redis 实例、同一 DB、同一密码 - 如果用了
symfony/messenger的doctrinetransport,确认数据库连接池未被 FrankenPHP 的 HTTP worker 占满
最易忽略的是:FrankenPHP 默认以 www-data 用户运行,而 Supervisor 的 php bin/console 若用 root 启动,可能读不到 .env 里的敏感变量,或者因 home 目录权限导致 Composer autoloader 加载失败。



















