批量发送邮件解决方案
首先批量发邮件肯定不能是循环得让用户等待发送完毕
我有两种实现,其实说白了本质都是一样得
1.定时器+队列
2.定时器+数据库表
还有一种我没有尝试,不过我个人感觉可能不太好,如果有大佬希望可以指点一二
那就是新开一个线程异步得一直发送,主线程则直接返回一个结果给用户即可(描述不精准勿怪)
我两种方式都采用了得,不过我得场景用数据库表更为恰当舒适,我就仅贴数据库表+定时器得实现代码了。
我是用laravel实现的
先使用命令php artisan make:command ProcessEmailQueue创建一个新的 Artisan 命令
首先先把需要的数据全部查出来存入数据库中
public function sendEmail()
{
$authRepositories = new AuthRepositories();
$historyRepositories = new HistoryRepositories();
$userStr = $this->input['userIds'];
$id = $this->input['historyId'];
$content = $this->input['content'] ?? '';
$type = $this->input['type'] ?? 0; //0-正常发送 1-定时发送
$timing = $this->input['timing'] ?? null;
if ($timing) {
$timing = date('Y-m-d H:i:s', strtotime($timing));
}
$model = $historyRepositories->findVersion($id);
$now = $model->now;
$fileId = $model->file_id;
$Filename = $model->label;
$userIds = explode(',', $userStr);
$users = $authRepositories->selectIn($userIds);
$time = $model->updated_at;
$bulkData = [];
foreach ($users as $user) {
$bulkData[] = [
'username' => $user->username,
'email' => $user->email,
'now' => $now,
'content' => $content,
'timing' => $type == 1 ? $timing : null,
'type' => $type,
'time' => $time,
'filename' => $Filename,
'fileid' => $fileId
];
}
EmailSendModel::insert($bulkData);
}
然后写定时任务(其实也就是刚刚创建一个新的 Artisan 命令类,laravel定时器我是通过这个命令类+crontab结合实现的)
class ProcessEmailQueue extends Command //即时批量发送
{
protected $signature = 'email:process-queue';
protected $description = 'Process the regular email queue';
/**
* Execute the console command.
*
*/
public function handle(): void
{
set_time_limit(25);
$fileRepositories = new FileRepositories();
$reopository = new sendEmailReopository();
$emailSend = EmailSendModel::query()->where('type', 0)->where('status', 0)->limit(10)->get();
$success = [];
$error = [];
foreach ($emailSend as $model) {
$collection = 'fileHtml' . $model->fileid;
$historyHtml = $fileRepositories->findMongo($collection, $model->now)['content'] ?? '';
$res = $reopository->send($model->username, $historyHtml, $model->filename, $model->time, $model->email);
if ($res) {
$success[] = $model->id;
} else {
$error[] = $model->id;
}
}
if (!empty($success)) {
EmailSendModel::query()->whereIn('id', $success)->update(['status' => 1]);
}
if (!empty($error)) {
EmailSendModel::query()->whereIn('id', $error)->update(['status' => 2]);
}
}
}
class ProcessTimingEmailQueue extends Command //定时发送
{
protected $signature = 'email:process-timing-queue';
protected $description = 'Process the timing email queue';
/**
* Execute the console command.
*
*/
public function handle(): void
{
set_time_limit(25);
$fileRepositories = new FileRepositories();
$reopository = new sendEmailReopository();
$emailSend = EmailSendModel::query()
->where('type', 1)
->where('status', 0)
->where('timing', '<', Carbon::now('Asia/Shanghai'))
->limit(10)
->get();
$success = [];
$error = [];
foreach ($emailSend as $model) {
$collection = 'fileHtml' . $model->fileid;
$historyHtml = $fileRepositories->findMongo($collection, $model->now)['content'] ?? '';
$res = $reopository->send($model->username, $historyHtml, $model->filename, $model->time, $model->email);
if ($res) {
$success[] = $model->id;
} else {
$error[] = $model->id;
}
}
if (!empty($success)) {
EmailSendModel::query()->whereIn('id', $success)->update(['status' => 1]);
}
if (!empty($error)) {
EmailSendModel::query()->whereIn('id', $error)->update(['status' => 2]);
}
}
}
然后再让crontab任务隔一段时间执行一次即可
* * * * * cd /wwwroot/file-web && php artisan email:process-queue
* * * * * sleep 30 && cd /wwwroot/file-web && php artisan email:process-queue
* * * * * cd /wwwroot/file-web && php artisan email:process-timing-queue
* * * * * sleep 30 && cd /wwwroot/file-web && php artisan email:process-timing-queue