項(xiàng)目需要使用郵件拓展業(yè)務(wù),要用到隊(duì)列群發(fā)的功能,特此記錄一下
為了防止郵件發(fā)送過多導(dǎo)致信件發(fā)送失敗,同時(shí)使用了兩種解決方法
1、使用了多個(gè)郵箱批量發(fā)送郵件
2、限制了郵件發(fā)送任務(wù)的調(diào)用頻率。
首先創(chuàng)建一個(gè)隊(duì)列任務(wù):
php artisan make:job SendMail
隊(duì)列的環(huán)境配置請(qǐng)參考laravel的官方文檔
建議在開發(fā)環(huán)境下將.env中配置為 QUEUE_CONNECTION=sync 方便調(diào)試,生產(chǎn)環(huán)境下再配置為redis驅(qū)動(dòng)
<?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秒超時(shí)
/**
* 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()
{
//限制任務(wù)的調(diào)用:每20秒鐘只調(diào)用10次
Redis::throttle('key')->allow(10)->every(20)->then(function () {
try {
$this->handleEmail();//具體的發(fā)送郵件方法
} catch (\Exception $e) {
Log::error('queue error : ' . $e->getMessage());//打印錯(cuò)誤日志
}
}, function () {
return $this->release(10);
});
}
/**
* 發(fā)送郵件
*/
function handleEmail()
{
//備份原有Mailer
$backup = Mail::getSwiftMailer();
$user = $this->user; //獲取用戶的姓名
$email = $this->email;//獲取用戶的郵箱地址
$content = $this->content;//獲取郵件模板的內(nèi)容
//使用正則表達(dá)式替換相應(yīng)的用戶的姓名
$email_content = preg_replace('/\{\{s\}\}/', $user->name, $content->content); //模板內(nèi)容
$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}}
// 設(shè)置郵箱賬號(hào),支持qq郵箱/163郵箱/谷歌郵箱/騰訊企業(yè)郵箱
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);
});
// 發(fā)送后還原
Mail::setSwiftMailer($backup);
}
}
由于限制了隊(duì)列的調(diào)用頻率,所以任務(wù)失敗的次數(shù)是無法計(jì)算的,需要配合超時(shí)來做
我將任務(wù)超時(shí)時(shí)間設(shè)置為120秒
隊(duì)列的調(diào)用
public function store(Request $request)
{
..........
foreach ($job->user as $user) {
$email = $emails->random(); //隨機(jī)一個(gè)郵箱
//傳遞郵件的模板、郵箱和用戶作為一個(gè)單獨(dú)的發(fā)送任務(wù)
SendMail::dispatch($job_id, $template, $email, $user);
}
}