symfony 7 的 messenger 默认同步执行,需显式配置 transport 和路由才能异步入队;必须安装对应驱动、声明 transport dsn(含 queue_name)、运行迁移、设置路由、启动 consume 进程,并正确配置重试与失败队列。

Symfony 7 的 Messenger 不是开箱即用的队列,它默认只走同步传输(sync://),你看到消息“发出去了”但没进队列、没异步执行,大概率是因为没配传输或路由。
怎么让消息真进队列(比如 Doctrine 或 Redis)
Messenger 必须显式配置 transport 才能异步。光装包、光写消息类、光 dispatch,都不够。
- 先确认已安装 transport 驱动:用数据库就跑
composer require symfony/doctrine-messenger,用 Redis 就跑composer require symfony/redis-messenger - 在
config/packages/messenger.yaml里声明 transport,例如:transports: async: 'doctrine://default?queue_name=async'
注意 DSN 中的queue_name参数必须存在,Doctrine transport 依赖它建表 - 运行
php bin/console doctrine:migrations:diff && php bin/console doctrine:migrations:migrate确保messenger_messages表已创建 - 别漏掉路由配置——这是最容易卡住的一步:
framework: messenger: routing: 'Symfony\Component\Mailer\Messenger\SendEmailMessage': 'async' 'App\Message\ImportLargeFile': 'async'没这条,消息会 fallback 到sync://,看着 dispatch 成功,其实当场执行
为什么 dispatch 后邮件没发、任务没跑
常见错因不是代码写错了,而是消费进程根本没启动,或者配置没生效。
-
php bin/console messenger:consume async必须手动运行,且不能加--limit=1之类参数就退出——它得常驻监听 - 检查当前环境是否启用了 Messenger:运行
php bin/console debug:messenger,输出里要有asynctransport 和对应路由,否则配置没加载 - 如果你用的是
Symfony\Component\Mailer\Messenger\SendEmailMessage,别手写自己的邮件消息类——Messenger 只识别这个类名,自定义类不会被自动处理 - 消费时若报
Class "Symfony\Component\Mailer\Email" not found,通常是序列化器问题:确保没全局覆盖serializer.default_context为 JSON;Doctrine transport 在 PHP 8.5.7 下默认用PhpSerializer,改用 JSON 会导致类型信息丢失
重试和失败队列怎么配才不丢消息
retry_strategy 和 failure_transport 是两套独立配置,混在一起就失效。
- 重试必须嵌在 transport 内部,不能写在
framework.messenger根层级:transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy: max_retries: 3 delay: 1000 multiplier: 2漏掉delay会直接报错 - failure_transport 要单独指向另一个 queue,比如:
failed: 'doctrine://default?queue_name=failed'
千万别和 async 共用同一个 DSN,否则失败消息会被 consumer 反复拉取,形成死循环 - 重试只由未捕获异常触发:handler 方法里
throw new \RuntimeException()才重试;return false / null / throw new LogicException()(非 RuntimeException)都不会重试 - 验证失败队列是否工作:故意让 handler 抛异常,然后运行
php bin/console messenger:failed:show,有输出才算真正进了 DLQ
最常被忽略的点是:Messenger 的 transport 配置受环境影响,.env 里的 MESSENGER_TRANSPORT_DSN 在 test 环境下可能为空,导致本地开发看似正常,一上 prod 就退化成同步;还有就是没意识到 messenger:consume 进程需要 systemd 或 supervisor 管理,不能只靠开发时手动跑一次。











