messenger异步失效主因是路由未配对:routing键必须为消息类全名(如app\message\sendemailnotification),而非handler类名;需用debug:messenger确认transport列为async,且dsn路径、序列化数据、transport连通性均合规。

消息发出去就同步执行,不是你代码写错了,而是 Messenger 的路由根本没配对——90% 的“异步失效”问题卡在这一步。
routing 键必须是消息类全名,不是 Handler 类名
Messenger 路由只看 $message 实例的 get_class($message) 结果,跟 MessageHandler 类完全无关。写错一个字符,它就回退到默认 sync transport。
- 正确示例:
App\Message\SendEmailNotification: async - 常见错误:
App\MessageHandler\SendEmailNotificationHandler: async或App\Message\*: async - 运行
php bin/console debug:messenger,确认对应行的transport列明确显示async;为空、sync或压根不出现,说明没命中 - 检查命名空间与文件路径是否一致:类定义在
src/Message/SendEmail.php,但命名空间是App\Messages?那路由就必须写App\Messages\SendEmail
transport DSN 的路径部分不是装饰,是关键标识
Redis DSN 中的 /myapp_async 是 list key 名;RabbitMQ 中的 /my_exchange(经 URL 编码后为 %2fmy_exchange)是 exchange 名。写错或复用,轻则消息丢进空队列,重则报 ChannelException。
- 本地开发优先用 Redis:
MESSENGER_TRANSPORT_DSN=redis://localhost:6379/myapp_async - 多个项目共用同一 Redis 实例?必须加唯一前缀,否则
/messages会被互相覆盖 - RabbitMQ 用户需提前在管理后台创建好对应 exchange,并确保 vhost(DSN 中
amqp://user:pass@host:5672/%2f的%2f)匹配 - 运行
php bin/console messenger:transport:setup-transports,看到Transport "async" is ready!才算连通
消息类里只能放可序列化的原始数据
传 $userEntity、Closure、stdClass 或未声明的私有属性,序列化阶段可能静默失败,反序列化时直接炸: Unserialization of 'Doctrine\ORM\Proxy\__CG__' is not allowed。
- 允许类型:字符串、整型、布尔、浮点、数组、
DateTimeInterface实例 - 禁止类型:
Entity对象、Repository、resource、Closure、stdClass、PHP 8.2+ 的只读类(除非显式加[Serializable]且 PHP ≥ 8) - 安全做法:只传 ID 或必要字段,消费者里再查库 —— 比如
new SendEmailNotification($userId, $templateKey),而不是new SendEmailNotification($userEntity)
Symfony 7 的虚拟线程不替代 Messenger,而是协同增强消费端
VirtualThread 或 Swoole 协程不是让 dispatch() 变异步的开关,它优化的是 messenger:consume 进程里的消息处理并发度。主线程发消息仍走传统 HTTP 生命周期,异步与否只取决于 Messenger 配置是否生效。
- 想提升消费吞吐?用 Swoole 启动协程版消费者:
php bin/console messenger:consume async --transport=swoole(需额外配置 swoole.yaml) - 别指望改个
VirtualThread::create()就绕过 routing 和 transport 验证 —— Messenger 的路由规则、序列化约束、transport 连通性,一条都不能跳过 - 真正容易被忽略的点:即使启用了协程运行时,如果
debug:messenger显示 transport 是sync,消息照样当场执行,协程根本没机会介入











