Laravel队列批量发送邮件,限制调用频率 支持QQ邮箱/16
2020-04-20 本文已影响0人
众神开挂
项目需要使用邮件拓展业务,要用到队列群发的功能,特此记录一下
为了防止邮件发送过多导致信件发送失败,同时使用了两种解决方法
1、使用了多个邮箱批量发送邮件
2、限制了邮件发送任务的调用频率。
首先创建一个队列任务:
php artisan make:job SendMail
队列的环境配置请参考laravel的官方文档
建议在开发环境下将.env中配置为 QUEUE_CONNECTION=sync 方便调试,生产环境下再配置为redis驱动
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Redis;
class SendMail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $content;
protected $email;
protected $user;
protected $job_id;
public $timeout =120; //120秒超时
/**
* Create a new job instance.
*
* @param $job_id
* @param $content
* @param $email
* @param $user
*/
public function __construct($job_id, $content, $email, $user)
{
$this->content = $content;
$this->email = $email;
$this->user = $user;
$this->job_id = $job_id;
}
/**
* Execute the job.
* @return void
*/
public function handle()
{
//限制任务的调用:每20秒钟只调用10次
Redis::throttle('key')->allow(10)->every(20)->then(function () {
try {
$this->handleEmail();//具体的发送邮件方法
} catch (\Exception $e) {
Log::error('queue error : ' . $e->getMessage());//打印错误日志
}
}, function () {
return $this->release(10);
});
}
/**
* 发送邮件
*/
function handleEmail()
{
//备份原有Mailer
$backup = Mail::getSwiftMailer();
$user = $this->user; //获取用户的姓名
$email = $this->email;//获取用户的邮箱地址
$content = $this->content;//获取邮件模板的内容
//使用正则表达式替换相应的用户的姓名
$email_content = preg_replace('/\{\{s\}\}/', $user->name, $content->content); //模板内容
$email_theme = preg_replace('/\{\{s\}\}/', $user->name, $content->theme); //邮件主题
$email_sign = preg_replace('/\{\{s\}\}/', $user->name, $content->sign);; //邮件签名
$email_sign = preg_replace('/\{\{t\}\}/', date('Y-m-d'), $email_sign);; //邮件签名{{t}}
// 设置邮箱账号,支持qq邮箱/163邮箱/谷歌邮箱/腾讯企业邮箱
if ($email->type == 'qq') {
$transport = new \Swift_SmtpTransport('smtp.qq.com', 465, 'ssl');
} else if ($email->type == 'gmail') {
$transport = new \Swift_SmtpTransport('smtp.gmail.com', 465, 'ssl');
} else if ($email->type == '163') {
$transport = new \Swift_SmtpTransport('smtp.163.com', 465, 'ssl');
} else if ($email->type == 'exmail') {
$transport = new \Swift_SmtpTransport('smtp.exmail.qq.com', 465, 'ssl');
} else {
throw new AdminApiException('邮箱类型不支持!');
}
$transport->setUsername($email->email);
$transport->setPassword($email->secret);
$mailer = new \Swift_Mailer($transport);
Mail::setSwiftMailer($mailer);
Mail::send('emails.kol.index',
['content' => $email_content, 'email_sign' => $email_sign],
function ($message) use ($email_theme, $user, $email) {
$message->from($email->email, $email->email);
$message->subject($email_theme);
$message->to($user->email);
});
// 发送后还原
Mail::setSwiftMailer($backup);
}
}
由于限制了队列的调用频率,所以任务失败的次数是无法计算的,需要配合超时来做
我将任务超时时间设置为120秒
队列的调用
public function store(Request $request)
{
..........
foreach ($job->user as $user) {
$email = $emails->random(); //随机一个邮箱
//传递邮件的模板、邮箱和用户作为一个单独的发送任务
SendMail::dispatch($job_id, $template, $email, $user);
}
}