symfony messenger默认不启用异步,必须显式配置transport和routing,否则dispatch()走sync://同步执行;需安装驱动、声明dsn、运行迁移、设置路由、启动consume进程,并确保消息类可序列化。

为什么 Messenger 不等于「开箱即用的异步队列」
直接把消息发给 Messenger 并不会自动进队列——它默认走同步传输。你看到消息被“发送”了,其实只是调用了处理器(MessageHandler)的 __invoke(),全程在当前请求里执行,根本没碰 RabbitMQ 或 Redis。
真正触发异步,必须显式配置传输(transport)并启用 dispatch 的异步模式。常见错误是只配了 framework.messenger.transports.async,却忘了在 dispatch() 时指定 transport 或打上 #[Asynchronous] 属性。
-
Messenger默认 transport 是sync,哪怕你定义了asynctransport,不指定就不用 - 使用
#[Asynchronous]要求 PHP ≥ 8.0,且类必须被容器管理(不能 new 出来) - 若 handler 抛异常,而 transport 是失败重试型(如 SQS),消息可能卡在 retry queue 里,需手动干预
如何让 dispatch() 真正走异步 transport
最稳妥的方式是在 dispatch 时强制指定 transport 名,而不是依赖注解或自动推断。这样逻辑清晰、调试方便,也避免因注解未生效导致“以为异步实则同步”的线上事故。
- 在 service 配置中确保 transport 已启用:例如
amqp://guest:guest@localhost对应amqptransport - 代码中显式 dispatch:
use Symfony\Component\Messenger\MessageBusInterface;<br><br>$bus->dispatch(<br> new YourMessage('data')<br>)->onTransport('async'); - 如果用
#[Asynchronous],要确认该 message class 在容器中被 autoconfigure 且未被 exclude;可通过bin/console debug:messenger检查是否绑定到asynctransport
Worker 启动后没消费?检查这三件事
运行 php bin/console messenger:consume async 却没日志、没处理,大概率不是代码问题,而是 transport 层卡住了。
- transport 是否连得上?比如 RabbitMQ 服务没启、凭据错、vhost 不存在——
messenger:consume默认静默失败,加-vv才能看到连接拒绝错误 - exchange/queue 是否已声明?某些 transport(如 AMQP)要求先存在 queue,否则
consume会报ChannelException;可设auto_setup: true让 Messenger 自动建(仅开发环境推荐) - message class 是否被 handler 映射?
debug:messenger输出里看不到 handler 绑定,说明handle方法签名不对,或MessageHandlerInterface实现漏了
并发控制:别只调 --limit,还要看 transport 特性
--limit=10 只限制本次 consume 命令最多处理 10 条,不是并发数。真正决定并发的是 transport 的底层连接模型和 worker 进程数。
- RabbitMQ 场景下,并发由
--threads(PHP 8.1+)或进程数(supervisord多实例)控制,每线程/进程独占一个 AMQP channel - Redis transport 使用
blpop,本质单连接阻塞读,--threads无效;想提并发就得跑多个messenger:consume进程 -
--time-limit=300和--memory-limit=128M更关键:防止长任务拖垮 worker,也避免内存泄漏累积
实际部署时,transport 的吞吐瓶颈往往早于 PHP worker 本身——比如 RabbitMQ 的 connection limit、Redis 的 maxclients,这些得去中间件侧调,光调 --threads 没用。











