34

Swoole实验室:3-使用Swoole批量发送邮件

 5 years ago
source link: https://www.helloweba.net/php/582.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在上一节中我们介绍了使用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' => '[email protected]', //接收方,改成自己的邮箱可以测试接收邮件
                    '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 批量发邮件指令。

<?php 
class Client
{
    private $client;
    
    public function __construct() {
        $this->client = new swoole_client(SWOOLE_SOCK_TCP);
    }

    public function connect() {
        if( !$this->client->connect("127.0.0.1", 9502 , 1) ) {
            echo "Error: {$this->client->errMsg}[{$this->client->errCode}]\n";
        }
        
        $action = 'sendMailQueue';
        $time = time();
        $key = 'MYgGnQE33ytd2jDFADS39DSEWsdD24sK';
        $token = md5($action.$time.$key);
        $data = [
            'action' => $action,
            'token' => $token,
            'timestamp' => $time
        ];
        $msg = json_encode($data);

        $this->client->send( $msg );
        $message = $this->client->recv();
        echo "Get Message From Server:{$message}\n";
    }
}


$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$password = '123';
$redis->auth($password);

$arr = [];

$arr[0] = [
    'subject' => '国庆大酬宾,全场1折',
    'emailAddress' => '[email protected]',
    'body' => '您好,国庆期间大酬宾,全场所有商品统统1折甩卖。'
];
$arr[1] = [
    'subject' => '注册会员送100金币',
    'emailAddress' => '[email protected]',
    'body' => '邮件内容'
];
$arr[2] = [
    'subject' => '国庆大酬宾,全场1折',
    'emailAddress' => '[email protected]',
    'body' => '邮件内容2'
];
      
foreach ($arr as $k=>$v) {
    $redis->rpush("mailerlist", json_encode($v, JSON_UNESCAPED_UNICODE));
}


$client = new Client();
$client->connect();

验证

根据上一节内容,我们应该先启动服务端,看到Swoole服务端启动好了,我们再运行客户端:

php mailClient.php

然后你可以去查看对方邮箱是否收到相关邮件。

本文中使用了redis作为简单队列,你也可以使用复杂点的队列rabbitmq。你也可以使用Crontab来做定时任务,不过它最小粒度是分钟级别的。当然对于批量发送邮件,如果你不用php的话,可以用Python或者Java,它们都有相当成熟的解决方案。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK