在上一节中我们介绍了使用Swoole发送单个邮件,那么如果是大量的邮件需要发送,比如给2万个用户发送优惠活动邮件,这是一个比较耗时的过程,而PHP本身不适合处理这种耗时多任务场景。本节为给大家介绍使用Swoole+Redis来实现发送批量邮件的例子。
建立任务
接上一节代码,编辑src/App/Mail.php文件代码,在public function onTask()方法中增加批量队列发送邮件的代码:
public function onTask(swoole_server $serv, $task_id, $from_id, $data)
{
$res['result'] = 'failed';
$req = json_decode($data, true);
$action = $req['action'];
echo date('Y-m-d H:i:s')." onTask: [".$action."].\n";
switch ($action) {
case 'sendMail': //发送单个邮件
$mailData = [
'emailAddress' => 'abc@example.com', //接收方,改成自己的邮箱可以测试接收邮件
'subject' => 'swoole实验室',
'body' => '测试This is the HTML message body.',
'attach' => '/home/swoole/public/a.jpg'
];
$this->sendMail($mailData);
break;
case 'sendMailQueue': // 批量队列发送邮件
$this->sendMailQueue();
break;
default:
break;
}
}
建立Redis队列
由于发送的邮件比较多,我们把邮件列表事先保存在Redis队列中。我们知道Redis的使用场景很多,其中就可以用它来做简单的队列。
我们在任务中调用了sendMailQueue()方法,继续在Mail.php中添加:
// 邮件发送队列
private function sendMailQueue()
{
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$password = '123';
$redis->auth($password);
swoole_timer_tick(1000, function($timer) use ($redis) { // 启用定时器,每1秒执行一次
$value = $redis->lpop('mailerlist');
if($value){
//echo '获取redis数据:' . $value;
$json = json_decode($value, true);
$start = microtime(true);
$rs = $this->sendMail($json);
$end = microtime(true);
if ($rs) {
echo '发送成功!'.$value.', 耗时:'. round($end - $start, 3).'秒'.PHP_EOL;
} else { // 把发送失败的加入到失败队列中,人工处理
$redis->rpush("mailerlist_err", $value);
}
}else{
swoole_timer_clear($timer); // 停止定时器
echo "Emaillist出队完成";
}
});
}
上述代码中,先尝试连接Redis,然后使用Swoole的swoole_timer_tick()函数,它是个定时器,这个函数跟js的interval()函数一样,意思是每隔一定时间执行一次,它可以定义毫秒级粒度。很显然,上述代码中,每隔1000毫秒(1秒)从Redis队列mailerlist中取出一条,即一个邮件对象,然后执行发送邮件sendMail(),当发送完所有邮件后,使用swoole_timer_clear()关闭定时器即可。定时器的间隔时间可以调整。
客户端
在客户端,我们先往Redis队列里添加邮件内容,然后向服务端发起sendMailQueue批量发邮件指令。