Laravel 9中每20封邮件新建连接发送通讯的实现方案求助
解决Laravel邮件单会话发送限制问题
针对服务商限制单会话最多发送20封邮件的情况,给你几个可行的实现方案:
方案一:使用SwiftMailer自带的AntiFlood插件(最简洁)
SwiftMailer内置了AntiFloodPlugin插件,专门用来处理这类发送频率/会话限制,它会自动在发送指定数量邮件后断开SMTP连接并重新建立,还能设置批次间的延迟。
实现步骤:
- 在
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); }
- 优化原命令中的查询,避免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封邮件发送后手动断开连接。
实现步骤:
- 创建批次处理任务:
php artisan make:job SendReminderBatch
- 编写任务逻辑:
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(); } }
- 修改原命令,拆分用户并分发批次任务:
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
相关产品推荐
相关产品推荐

