Symfony Messenger需显式启用Bundle、精确配置消息类全名路由、正确设置传输DSN并验证连通性,最后启动消费者进程,否则消息将同步执行而非异步。

Symfony 本身不自动异步,必须靠 Messenger 组件把任务“发进队列”,再由后台消费者取出来执行。关键不是“怎么发”,而是“怎么确保它真走异步”——配置错一个字母,消息就当场同步执行。
一、装好组件并启用 Bundle
只运行 composer require symfony/messenger 不够。MessengerBundle 必须被显式启用:
- 检查
config/bundles.php是否包含:Symfony\Bundle\MessengerBundle\MessengerBundle::class => ['all' => true] - 运行
php bin/console debug:bundles | grep Messenger,有输出才算生效
二、配对路由:键名必须是消息类全名
这是最常出错的地方。Messenger 只认你 dispatch() 的那个类,不看处理器、不猜命名空间:
- 正确写法(假设消息类是
App\Message\SendInvoiceEmail):App\Message\SendInvoiceEmail: async - 错误写法:
App\MessageHandler\SendInvoiceEmailHandler: async或SendInvoiceEmail: async - 运行
php bin/console debug:messenger,确认该类 Transport 列显示 async,不是空或 sync
三、选好传输并验证连通性
没传输,消息就无处可去。DSN 配置和实际服务要一致:
- 在
.env中设:MESSENGER_TRANSPORT_DSN=redis://localhost:6379/myapp_async
(推荐 Redis;用 Doctrine 需先建表:php bin/console doctrine:migrations:generate) - 运行
php bin/console messenger:transport:setup-transports,看到 Transport "async" is ready! 才算通 - 注意 DSN 路径部分(如
/myapp_async)要唯一,避免多个项目混用 Redis 时串队
四、发消息 + 启动消费者
发只是第一步,没消费者,消息永远躺在队列里:
- 在控制器中 dispatch 消息:
$this->messenger->dispatch(new SendInvoiceEmail($invoiceId)); - 本地测试消费一条:
php bin/console messenger:consume async --limit=1 --verbose - 生产环境必须守护运行:
php bin/console messenger:consume async --time-limit=3600 --failure-limit=3
建议用 Supervisor 管理进程


















