
本文旨在提供在 symfony 5 应用程序中灵活实现邮件同步与异步发送的详细指南。针对默认 messenger 配置可能导致所有邮件异步化或自定义服务引发重复发送的问题,文章将深入讲解如何通过创建自定义消息类和处理器,并结合 messenger 消息总线,实现对异步邮件的精确控制,同时保持同步邮件的直接发送能力,从而优化应用性能与用户体验。
在现代 Web 应用程序中,邮件发送是常见的业务需求。然而,直接发送邮件可能是一个耗时的操作,尤其是在高并发场景下,这会阻塞主请求并影响用户体验。Symfony 框架通过其 Mailer 组件和 Messenger 组件提供了强大的邮件发送能力,包括对异步发送的支持。本文将详细介绍如何在 Symfony 5 应用程序中同时实现同步和异步邮件发送,确保灵活性和高效性。
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 总线。
要实现异步邮件发送,核心思想是创建一个自定义的消息类来承载邮件所需的数据,以及一个对应的消息处理器来执行实际的邮件发送逻辑。
这个消息类将作为数据传输对象 (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;
}
}消息处理器负责接收 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);
}
}注意:
现在,我们需要告诉 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 # 移除或注释此行在你的服务中,注入 MessageBusInterface 来调度 EmailAsync 消息。
// src/Service/MailManagerAsync.php
<?php
namespace App\Service;
use App\Message\EmailAsync;
use Symfony\Component\Messenger\MessageBusInterface;
class MailManagerAsync
{
private MessageBusInterface $bus;
public function __construct(MessageBusInterface $bus)
{
$this->bus = $bus;
}
public function sendAsyncEmail(
string $subject,
string $htmlTemplate,
?string $textTemplate,
string $to,
array $context = [],
string $from = 'noreply@example.com'
): void {
$emailAsync = new EmailAsync($subject, $htmlTemplate, $textTemplate, $to, $context, $from);
$this->bus->dispatch($emailAsync);
}
}现在,当你需要发送异步邮件时,只需调用 MailManagerAsync::sendAsyncEmail() 方法,邮件数据将被封装成 EmailAsync 消息并投递到 Messenger 队列。
对于同步邮件,过程非常简单。由于我们已经移除了 Symfony\Component\Mailer\Messenger\SendEmailMessage 的异步路由,直接使用 MailerInterface 发送邮件将是同步的,它不会被 Messenger 拦截并放入队列。
// src/Service/MailManagerSync.php
<?php
namespace App\Service;
use Symfony\Component\Mime\Address;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
class MailManagerSync
{
private MailerInterface $mailer;
private string $defaultFromEmail; // 可以从配置中注入
public function __construct(MailerInterface $mailer, string $defaultFromEmail = 'noreply@example.com')
{
$this->mailer = $mailer;
$this->defaultFromEmail = $defaultFromEmail;
}
public function sendSyncEmail(
string $subject,
string $htmlTemplate,
?string $textTemplate,
string $to,
array $context = [],
?string $from = null
): void {
$email = (new TemplatedEmail())
->from(new Address($from ?? $this->defaultFromEmail))
->to(new Address($to))
->subject($subject)
->htmlTemplate($htmlTemplate)
->textTemplate($textTemplate)
->context($context);
$this->mailer->send($email); // 这将是同步发送
}
}何时选择同步/异步:
错误处理与重试:
邮件模板管理:
发件人配置:
Messenger Worker:
php bin/console messenger:consume async
日志记录:
通过为异步邮件创建自定义消息和处理器,并结合 Messenger 消息总线进行调度,我们可以在 Symfony 5 应用程序中实现对邮件发送模式的精细控制。这种方法允许应用程序在需要时利用异步处理来提升性能和用户体验,同时保留直接发送同步邮件的能力,从而构建出更健壮、更灵活的邮件发送系统。理解并正确配置 Messenger 是实现这一目标的关键。
以上就是Symfony 5 邮件发送:同步与异步模式的实践指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号