Symfony 5.4下PHP Mailer异步发送邮件解决请求阻塞问题求助
解决Symfony 5.4中邮件发送阻塞请求的问题
问题背景
订单状态变更时给客户发邮件,单封邮件耗时5-15秒,导致用户在邮件发送完成前无法发起新请求。把邮件暂存到数据库后用Ajax批量发送,PHP还是会阻塞新请求;尝试curl_multi调用自身端点也没解决,需要在Symfony 5.4.27(PHP 8.2、PHPMailer)环境下实现非阻塞发送。
可行解决方案
1. 用Symfony Messenger做异步邮件处理
这是Symfony官方推荐的异步任务方案,适配5.4版本,能彻底解决阻塞问题:
- 安装Messenger组件
composer require symfony/messenger - 配置消息队列(用Doctrine做存储,适合中小项目)
在config/packages/messenger.yaml里添加:
然后在framework: messenger: transports: async: '%env(MESSENGER_TRANSPORT_DSN)%' routing: 'App\Message\SendOrderStatusEmail': async.env里设置DSN:MESSENGER_TRANSPORT_DSN=doctrine://default - 创建邮件消息类
// src/Message/SendOrderStatusEmail.php namespace App\Message; class SendOrderStatusEmail { private int $orderId; public function __construct(int $orderId) { $this->orderId = $orderId; } public function getOrderId(): int { return $this->orderId; } } - 写消息处理器
// src/MessageHandler/SendOrderStatusEmailHandler.php namespace App\MessageHandler; use App\Message\SendOrderStatusEmail; use Symfony\Component\Messenger\Handler\MessageHandlerInterface; use PHPMailer\PHPMailer\PHPMailer; class SendOrderStatusEmailHandler implements MessageHandlerInterface { // 可注入订单仓库等服务 public function __invoke(SendOrderStatusEmail $message) { $orderId = $message->getOrderId(); // 从数据库拉取订单、客户信息 // 初始化PHPMailer并发送邮件 $mail = new PHPMailer(true); // 此处编写邮件配置、内容填充和发送逻辑 } } - 订单状态变更时发消息
在控制器或业务服务里,把发送邮件的逻辑换成派发消息:$this->messenger->dispatch(new SendOrderStatusEmail($order->getId())); - 启动消息消费者
在命令行运行以下命令,让它后台处理队列里的邮件任务:
生产环境可以用Supervisor工具把这个进程设为常驻,避免手动启动。php bin/console messenger:consume async
2. 用pcntl_fork创建子进程发送邮件
如果不想用消息队列,服务器又支持pcntl扩展,可以直接创建子进程处理邮件,父进程立即返回:
// 在你的邮件发送逻辑里 $pid = pcntl_fork(); if ($pid === -1) { // 子进程创建失败,记日志告警 } elseif ($pid === 0) { // 子进程里执行邮件发送 $mail = new PHPMailer(true); // 邮件配置和发送逻辑 exit(0); // 子进程完成后退出,避免干扰父进程 } // 父进程直接继续执行,不等待子进程
注意:Symfony里用pcntl_fork要注意资源隔离,比如子进程最好重新初始化数据库连接,避免和父进程共享连接导致异常。
3. 换用第三方邮件服务API
比如SendGrid、Mailgun这类服务,它们的API响应速度极快(毫秒级),不会阻塞请求。PHPMailer也支持配置这些服务,只需要调整SMTP参数或者直接调用它们的SDK,把发送请求丢给第三方服务处理。
内容的提问来源于stack exchange,提问作者Bruno Souza
相关产品推荐
相关产品推荐

