You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 9中每20封邮件新建连接发送通讯的实现方案求助

解决Laravel邮件单会话发送限制问题

针对服务商限制单会话最多发送20封邮件的情况,给你几个可行的实现方案:

方案一:使用SwiftMailer自带的AntiFlood插件(最简洁)

SwiftMailer内置了AntiFloodPlugin插件,专门用来处理这类发送频率/会话限制,它会自动在发送指定数量邮件后断开SMTP连接并重新建立,还能设置批次间的延迟。

实现步骤:

  1. 在app/Providers/AppServiceProvider.php的boot方法中添加插件注册代码:
use Illuminate\Support\Facades\Mail;
use Swift_Plugins_AntiFloodPlugin;

public function boot()
{
    $mailer = Mail::getSwiftMailer();
    // 每发送20封后断开重连,批次间延迟5秒(可根据需要调整)
    $antiFloodPlugin = new Swift_Plugins_AntiFloodPlugin(20, 5);
    $mailer->registerPlugin($antiFloodPlugin);
}
  1. 优化原命令中的查询,避免N+1问题:
public function handle()
{
    // 预加载关联的User模型,避免循环内重复查询
    $pendingUsers = TargetUser::with('user')
        ->whereDate('reminder_date', today())
        ->get();

    foreach ($pendingUsers as $targetUser) {
        $user = $targetUser->user;
        Mail::to($user->email)->queue(new SendReminder($user));
    }
}

这样修改后,原有队列逻辑完全不用改,插件会自动处理连接重置,完美适配服务商的限制。

方案二:拆分批次为独立队列任务

如果不想依赖SwiftMailer插件,也可以把用户按20个一组拆分,每组作为一个独立的队列任务,任务内部完成20封邮件发送后手动断开连接。

实现步骤:

  1. 创建批次处理任务:
php artisan make:job SendReminderBatch
  1. 编写任务逻辑:
namespace App\Jobs;

use App\Models\TargetUser;
use App\Mail\SendReminder;
use Illuminate\Support\Facades\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SendReminderBatch implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $userIds;

    public function __construct(array $userIds)
    {
        $this->userIds = $userIds;
    }

    public function handle()
    {
        // 批量获取用户信息
        $targetUsers = TargetUser::with('user')
            ->whereIn('id', $this->userIds)
            ->get();

        foreach ($targetUsers as $targetUser) {
            $user = $targetUser->user;
            Mail::to($user->email)->send(new SendReminder($user));
        }

        // 发送完批次后,手动断开SMTP连接
        Mail::getSwiftMailer()->getTransport()->stop();
    }
}
  1. 修改原命令,拆分用户并分发批次任务:
public function handle()
{
    $pendingUsers = TargetUser::with('user')
        ->whereDate('reminder_date', today())
        ->get();

    // 每20个用户分一个批次
    $pendingUsers->chunk(20, function ($chunk) {
        // 提取批次内用户的ID,传递给任务
        $userIds = $chunk->pluck('id')->toArray();
        dispatch(new \App\Jobs\SendReminderBatch($userIds));
    });
}

每个批次任务会独立建立SMTP连接发送20封邮件,完成后断开,下一个任务会重新建立新连接,不会触发限制。

方案三:同步分块发送(适合小量用户)

如果用户量不大,也可以直接在命令中同步分块发送,每发送20封后断开连接并设置延迟:

public function handle()
{
    $pendingUsers = TargetUser::with('user')
        ->whereDate('reminder_date', today())
        ->get();

    $pendingUsers->chunk(20, function ($chunk) {
        foreach ($chunk as $targetUser) {
            $user = $targetUser->user;
            Mail::to($user->email)->send(new SendReminder($user));
        }

        // 断开SMTP连接
        Mail::getSwiftMailer()->getTransport()->stop();
        // 批次间延迟5秒,避免触发服务商的频率限制
        sleep(5);
    });
}

这种方式不需要队列,直接在命令执行时完成发送,但如果用户量很大,命令执行时间会很长,适合用户量较小的场景。


内容的提问来源于stack exchange,提问作者Wolfie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 01:47:04