Symfony 中用 Messenger 组件实现异步任务:安装组件后配置 transport 和路由,定义消息类与处理器,控制器中 dispatch 发送,再通过 messenger:consume 命令监听处理。

在 Symfony 项目中实现异步任务处理,比如发邮件、生成报表或调用第三方 API,需要一个可靠且可扩展的队列系统;Messenger 组件正是为此设计,它不自带消息代理,但能无缝对接 Redis、AMQP 或 Doctrine,帮你把耗时操作从 HTTP 请求中剥离出来。
安装并启用 Messenger 组件
执行命令安装 Messenger:composer require symfony/messenger。
安装后,Symfony Flex 会自动注册 messenger.yaml 配置文件;如果项目是旧版本或手动安装,需确认 config/packages/messenger.yaml 存在且已加载。
这一步不可跳过——【若未启用 Messenger,后续所有消息发送和处理都会静默失败,无报错提示】。
定义可发送的消息类
创建 src/Message/SendWelcomeEmail.php:
该类必须是纯 PHP 对象(POPO),不继承任何基类,不实现接口(Messenger 不强制要求),但建议加 final 和 readonly 属性以保证不可变性。
例如:final class SendWelcomeEmail { public function __construct(public string $email, public string $username) {} }
配置传输与路由规则
编辑 config/packages/messenger.yaml,先声明传输端点:
transports:
async: 'doctrine://default?queue_name=async'
再配置路由,告诉 Messenger 哪些消息走哪个传输:
routing:
'App\Message\SendWelcomeEmail': async
注意:transport 名称(如 async)必须与 transports 下定义的键完全一致,大小写敏感;【名称拼错会导致消息直接进入 failed transport 而非投递】。
从控制器发送消息
在控制器中注入 MessageBusInterface:
use Symfony\Contracts\Messenger\MessageBusInterface;
public function register(Request $request, MessageBusInterface $bus): Response {
$message = new SendWelcomeEmail('user@example.com', 'Alice');
$bus->dispatch($message);
return $this->redirectToRoute('success');
}
dispatch() 调用立即返回,不等待处理完成——这就是异步的核心。消息此时已序列化并存入配置的 transport(如数据库表或 Redis 队列)。
编写消息处理器
创建 src/MessageHandler/SendWelcomeEmailHandler.php:
#[AsMessageHandler] final class SendWelcomeEmailHandler implements MessageHandlerInterface {
public function __construct(private MailerInterface $mailer) {}
public function __invoke(SendWelcomeEmail $message): void {
$email = (new TemplatedEmail())
->to($message->email)
->subject('欢迎注册')
->htmlTemplate('email/welcome.html.twig')
->context(['name' => $message->username]);
$this->mailer->send($email);
}
}
处理器必须实现 __invoke 方法,参数类型需与消息类完全一致;Symfony 会自动根据类型匹配处理器。
启动消费者监听队列
第一步:确保 .env 中设置 MESSENGER_TRANSPORT_DSN=doctrine://default
第二步:执行命令启动监听器:php bin/console messenger:consume async --limit=10
第三步:加上 --time-limit=3600 可限制运行一小时后自动退出,避免长进程失控。
第四步:生产环境务必用 supervisor 或 systemd 管理该命令,否则终端关闭即停止消费。


















