
本文旨在提供在 symfony 5 应用程序中灵活实现邮件同步与异步发送的详细指南。针对默认 messenger 配置可能导致所有邮件异步化或自定义服务引发重复发送的问题,文章将深入讲解如何通过创建自定义消息类和处理器,并结合 messenger 消息总线,实现对异步邮件的精确控制,同时保持同步邮件的直接发送能力,从而优化应用性能与用户体验。
在现代 Web 应用程序中,邮件发送是常见的业务需求。然而,直接发送邮件可能是一个耗时的操作,尤其是在高并发场景下,这会阻塞主请求并影响用户体验。Symfony 框架通过其 Mailer 组件和 Messenger 组件提供了强大的邮件发送能力,包括对异步发送的支持。本文将详细介绍如何在 Symfony 5 应用程序中同时实现同步和异步邮件发送,确保灵活性和高效性。
理解 Symfony 邮件发送与 Messenger 集成
Symfony Mailer 组件默认与 Messenger 组件集成。当你使用 MailerInterface::send() 方法发送一个 Email 或 TemplatedEmail 实例时,Symfony 实际上会将其包装成一个 Symfony\Component\Mailer\Messenger\SendEmailMessage 消息,并将其调度到 Messenger 消息总线。
如果你的 Messenger 配置中包含以下路由规则:
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'Symfony\Component\Mailer\Messenger\SendEmailMessage': async登录后复制
这意味着所有通过 MailerInterface::send() 发送的邮件都将被路由到 async 传输层,从而实现异步发送。这种配置的缺点是,你无法直接发送同步邮件,因为所有邮件都将被放入队列。
为了实现同步和异步邮件的灵活控制,我们需要采用一种更细粒度的方法:为异步邮件创建自定义消息,并通过 Messenger 调度它们,而同步邮件则直接通过 MailerInterface 发送,不经过 Messenger 总线。
实现异步邮件发送:自定义消息与处理器
要实现异步邮件发送,核心思想是创建一个自定义的消息类来承载邮件所需的数据,以及一个对应的消息处理器来执行实际的邮件发送逻辑。
1. 创建异步邮件消息类
这个消息类将作为数据传输对象 (DTO),包含发送邮件所需的所有信息,例如主题、内容模板、收件人、上下文数据等。
// src/Message/EmailAsync.php
<?php
namespace App\Message;
class EmailAsync
{
private string $subject;
private string $bodyHtmlTemplate;
private ?string $bodyTextTemplate;
private string $recipient;
private array $context;
private string $senderEmail; // 假设发件人也是动态的或者需要传递
public function __construct(
string $subject,
string $bodyHtmlTemplate,
?string $bodyTextTemplate,
string $recipient,
array $context = [],
string $senderEmail = 'noreply@example.com' // 默认发件人
) {
$this->subject = $subject;
$this->bodyHtmlTemplate = $bodyHtmlTemplate;
$this->bodyTextTemplate = $bodyTextTemplate;
$this->recipient = $recipient;
$this->context = $context;
$this->senderEmail = $senderEmail;
}
// 提供所有属性的 getter 方法
public function getSubject(): string
{
return $this->subject;
}
public function getBodyHtmlTemplate(): string
{
return $this->bodyHtmlTemplate;
}
public function getBodyTextTemplate(): ?string
{
return $this->bodyTextTemplate;
}
public function getRecipient(): string
{
return $this->recipient;
}
public function getContext(): array
{
return $this->context;
}
public function getSenderEmail(): string
{
return $this->senderEmail;
}
}登录后复制
2. 创建异步邮件消息处理器
消息处理器负责接收 EmailAsync 消息,并使用 Symfony Mailer 组件实际构建和发送邮件。
// src/MessageHandler/EmailAsyncHandler.php
<?php
namespace App\MessageHandler;
use App\Message\EmailAsync;
use Symfony\Component\Mime\Address;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler; // Symfony 6+ 用属性,Symfony 5 用接口
// Symfony 5 兼容写法:实现 MessageHandlerInterface
// use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
// class EmailAsyncHandler implements MessageHandlerInterface
#[AsMessageHandler] // Symfony 6+ 推荐使用属性
class EmailAsyncHandler
{
private MailerInterface $mailer;
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
public function __invoke(EmailAsync $emailAsync): void
{
$emailToSend = (new TemplatedEmail())
->from(new Address($emailAsync->getSenderEmail()))
->to(new Address($emailAsync->getRecipient()))
->subject($emailAsync->getSubject())
->htmlTemplate($emailAsync->getBodyHtmlTemplate())
->textTemplate($emailAsync->getBodyTextTemplate())
->context($emailAsync->getContext());
$this->mailer->send($emailToSend);
}
}登录后复制
注意:
- 对于 Symfony 5,你需要让 EmailAsyncHandler 实现 Symfony\Component\Messenger\Handler\MessageHandlerInterface 接口。
- 对于 Symfony 6 及更高版本,可以使用 #[AsMessageHandler] 属性来自动注册处理器,无需实现接口。
3. 配置 Messenger 路由
现在,我们需要告诉 Messenger 如何路由我们的自定义 EmailAsync 消息。确保移除对 Symfony\Component\Mailer\Messenger\SendEmailMessage 的异步路由,以避免冲突。
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
# 可以有其他传输层,例如失败重试队列
# failed: 'doctrine://default?queue_name=failed'
routing:
# 将我们自定义的 EmailAsync 消息路由到异步传输层
'App\Message\EmailAsync': async
# 确保没有将 Symfony\Component\Mailer\Messenger\SendEmailMessage 路由到异步
# 否则所有邮件都将异步化,即使你只想同步发送
# 'Symfony\Component\Mailer\Messenger\SendEmailMessage': async # 移除或注释此行登录后复制

4. 通过 Messenger Bus 调度异步邮件
在你的服务中,注入 MessageBusInterface 来调度 EmailAsync 消息。
标签: php html 处理器 app 工具 ai 路由 kubernetes 密码重置
还木有评论哦,快来抢沙发吧~