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

在上一节中我们介绍了使用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批量发邮件指令。

<?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' => 'axxxx@example.com', 'body' => '您好,国庆期间大酬宾,全场所有商品统统1折甩卖。' ]; $arr[1] = [ 'subject' => '注册会员送100金币', 'emailAddress' => 'bxxxx@example.com', 'body' => '邮件内容' ]; $arr[2] = [ 'subject' => '国庆大酬宾,全场1折', 'emailAddress' => 'cxxxxx@example.com', '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,它们都有相当成熟的解决方案。加粗样式
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个完整的 Kubernetes YAML 文件示例,用于在 Kubernetes 中部署 Hyperf 应用: ```yaml # 定义一个 Kubernetes 的 Deployment apiVersion: apps/v1 kind: Deployment metadata: name: hyperf-deployment labels: app: hyperf spec: replicas: 3 selector: matchLabels: app: hyperf template: metadata: labels: app: hyperf spec: containers: - name: hyperf image: hyperf/hyperf:7.4-alpine-v3.11-swoole command: [ "sh", "-c", "php /hyperf-skeleton/bin/hyperf.php start" ] ports: - containerPort: 9501 volumeMounts: - name: hyperf-volume mountPath: /hyperf-skeleton volumes: - name: hyperf-volume hostPath: path: /mnt/hgfs/www/hyperf/ # 定义一个 Kubernetes 的 Service --- apiVersion: v1 kind: Service metadata: name: hyperf-service spec: selector: app: hyperf ports: - protocol: TCP port: 9501 targetPort: 9501 type: ClusterIP ``` 这个 YAML 文件定义了一个 Kubernetes 的 Deployment 和一个 Service。在 Deployment 中,我们定义了一个使用 Hyperf 容器镜像的 Pod 模板,使用 `volumeMounts` 字段将宿主机上 Hyperf 应用所在的目录 `/mnt/hgfs/www/hyperf/` 挂载到容器的 `/hyperf-skeleton` 目录下,并使用 `command` 字段指定容器启动 Hyperf 应用的命令 `php /hyperf-skeleton/bin/hyperf.php start`,使用 `ports` 字段将容器的 9501 端口暴露出来。在 Service 中,我们定义了一个 ClusterIP 类型的 Service,将容器的 9501 端口映射到 Kubernetes 集群内部的端口上,以便其他 Pod 可以通过 Service 访问 Hyperf 应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值