symfony messenger是围绕消息、总线、传输、处理器四环节构建的可配置系统,需按目标(后台任务/cqrs/解耦)差异化配置;安装后通过messenger.yaml定义传输、路由、消息与处理器,并用debug:messenger验证。

Symfony 消息总线(Messenger)不是“配好就能用”的黑盒,而是围绕消息、总线、传输、处理器四个核心环节组织的可配置系统。关键在于明确你要解决的问题:是想把耗时操作(如发邮件、生成报表)挪到后台?还是想严格分离命令与查询(CQRS)?抑或对接 RabbitMQ 实现服务解耦?不同目标,配置重心不同。
基础安装与默认总线启用
Messenger 组件默认不启用,需手动安装并配置:
- 运行 composer require symfony/messenger 安装核心包
- 若使用 Doctrine 作队列存储,加装 composer require symfony/doctrine-messenger
- 若用 Redis,加装 composer require symfony/redis-messenger
- 安装后,框架会自动创建 messenger.yaml 配置文件(位于 config/packages/)
定义传输(Transport)——消息存哪、怎么送
传输决定了消息是立即执行(同步),还是暂存队列异步处理。这是性能与可靠性取舍的关键一步:
- 同步传输(开发调试用):sync:// —— 消息直接调用处理器,无延迟但阻塞请求
- 异步传输(生产推荐):doctrine://default?queue_name=async(用数据库表)、redis://localhost:6379/messages 或 amqp://guest:guest@localhost/%2F(RabbitMQ)
- 在 messenger.yaml 中声明多个传输,例如:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
failed: 'doctrine://default?queue_name=failed'
路由消息到对应传输——让对的消息走对的路
靠 routing 配置决定哪些消息走异步、哪些走同步,甚至哪些进失败队列:
- 最简写法:'*': async —— 所有消息都进 async 队列
- 精准控制:'App\Message\SendEmailNotification': async
'App\Message\ImportLargeFile': failed - 也可按命名空间路由:'App\Message\': async
- 注意:未匹配到路由的消息,默认走同步传输(sync://),除非你显式禁用
创建消息类与处理器——业务逻辑落地点
消息是纯数据载体,处理器才是干活的人。两者通过类型自动绑定:
- 消息类只需是普通 PHP 对象,建议实现 MessageInterface(非强制,但利于类型安全)
- 处理器类用 #[AsMessageHandler] 属性标记,方法名任意(通常叫 __invoke)
- 示例:
class SendEmailNotification { public function __construct(public string $to, public string $subject) {} }
#[AsMessageHandler]
class SendEmailNotificationHandler {
public function __invoke(SendEmailNotification $message): void {
// 发送邮件逻辑
}
} - 无需手动注册服务——Messenger 会自动扫描并绑定同名消息与处理器
在代码中发送消息——触发执行
获取 message_bus 服务(默认总线)或指定总线(如 command.bus),调用 dispatch():
- $bus->dispatch(new SendEmailNotification('user@example.com', 'Welcome!'));
- 若用了多总线(如 CQRS 场景),可通过依赖注入获取特定总线:MessageBusInterface $commandBus
- dispatch 返回 Envelope,可用于添加元数据(如延迟投递、重试策略)
配置本身不复杂,但容易忽略传输是否真正可用、路由是否覆盖所有消息、处理器是否被正确扫描。跑起来前,用 bin/console debug:messenger 命令检查总线、传输、路由和处理器状态最稳妥。











