Symfony 5 邮件发送:同步与异步模式的实践指南

admin 百科 13

Symfony 5 邮件发送:同步与异步模式的实践指南

本文旨在提供在 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 # 移除或注释此行

登录后复制

Symfony 5 邮件发送:同步与异步模式的实践指南-第2张图片-佛山资讯网

4. 通过 Messenger Bus 调度异步邮件

在你的服务中,注入 MessageBusInterface 来调度 EmailAsync 消息。

标签: php html 处理器 app 工具 ai 路由 kubernetes 密码重置

发布评论 0条评论)

还木有评论哦,快来抢沙发吧~