44

Swoole实验室:2-使用Swoole发送邮件

 5 years ago
source link: https://www.helloweba.net/php/581.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可以执行异步操作,因此在web页面将请求提交给Swoole处理后,不用等待返回结果,页面也不会卡顿。Swoole在后台将耗时长的操作进行异步处理,从而改善用户体验,例如本节要给大家讲解的Swoole处理邮件。

下载源码

准备

请按照上一节: Swoole实验室:1-使用Composer构建项目 ,构建好项目。并使用composer安装邮件发送组件:phpmailer。

composer require phpmailer/phpmailer

主程序

在目录:src/App/下建立Mail.php,用作Swoole服务端主程序。

<?php 
namespace Helloweba\Swoole;

use swoole_server;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

class Mail
{
    protected $serv;
    protected $host = '127.0.0.1';
    protected $port = 9502;
    // 进程名称
    protected $taskName = 'swooleMailer';
    // PID路径
    protected $pidPath = '/run/swooleMail.pid';
    // 设置运行时参数
    protected $options = [
        'worker_num' => 4, //worker进程数,一般设置为CPU数的1-4倍  
        'daemonize' => true, //启用守护进程
        'log_file' => '/data/logs/swoole.log', //指定swoole错误日志文件
        'log_level' => 0, //日志级别 范围是0-5,0-DEBUG,1-TRACE,2-INFO,3-NOTICE,4-WARNING,5-ERROR
        'dispatch_mode' => 1, //数据包分发策略,1-轮询模式
        'task_worker_num' => 4, //task进程的数量
        'task_ipc_mode' => 3, //使用消息队列通信,并设置为争抢模式
        //'heartbeat_idle_time' => 600, //一个连接如果600秒内未向服务器发送任何数据,此连接将被强制关闭
        //'heartbeat_check_interval' => 60, //启用心跳检测,每隔60s轮循一次
    ];
    // 邮件服务器配置
    protected $mailConfig = [
        'smtp_server' => 'smtp.163.com',
        'username' => '[email protected]',
        'password' => '',// SMTP 密码/口令
        'secure' => 'ssl', //Enable TLS encryption, `ssl` also accepted
        'port' => 465, // tcp邮件服务器端口
    ];
    // 安全密钥
    protected $safeKey = 'MYgGnQE33ytd2jDFADS39DSEWsdD24sK';


    public function __construct($mailConfig, $options = [])
    {
        // 构建Server对象,监听端口
        $this->serv = new swoole_server($this->host, $this->port);

        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }
        $this->serv->set($this->options);

        $this->mailConfig = $mailConfig;

        // 注册事件
        $this->serv->on('Start', [$this, 'onStart']);
        $this->serv->on('Connect', [$this, 'onConnect']);
        $this->serv->on('Receive', [$this, 'onReceive']);
        $this->serv->on('Task', [$this, 'onTask']);  
        $this->serv->on('Finish', [$this, 'onFinish']);
        $this->serv->on('Close', [$this, 'onClose']);

        // 启动服务
        //$this->serv->start();
    }

    protected function init()
    {
        //
    }

    public function start()
    {
        // Run worker
        $this->serv->start();
    }

    public function onStart($serv)
    {
        // 设置进程名
        cli_set_process_title($this->taskName);
        //记录进程id,脚本实现自动重启
        $pid = "{$serv->master_pid}\n{$serv->manager_pid}";
        file_put_contents($this->pidPath, $pid);
    }

    //监听连接进入事件
    public function onConnect($serv, $fd, $from_id)
    {
        $serv->send($fd, "Hello {$fd}!" );
    }

    // 监听数据接收事件
    public function onReceive(swoole_server $serv, $fd, $from_id, $data)
    {
        $res['result'] = 'failed';
        $key = $this->safeKey;

        $req = json_decode($data, true);
        $action = $req['action'];
        $token = $req['token'];
        $timestamp = $req['timestamp'];

        if (time() - $timestamp > 180) {
            $res['code'] = '已超时';
            $serv->send($fd, json_encode($res));
            exit;
        }

        $token_get = md5($action.$timestamp.$key);
        if ($token != $token_get) {
            $res['msg'] = '非法提交';
            $serv->send($fd, json_encode($res));
            exit;
        }

        $res['result'] = 'success';
        $serv->send($fd, json_encode($res)); // 同步返回消息给客户端
        $serv->task($data);  // 执行异步任务

    }

    /**
    * @param $serv swoole_server swoole_server对象
    * @param $task_id int 任务id
    * @param $from_id int 投递任务的worker_id
    * @param $data string 投递的数据
    */
    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 <b>in bold!</b>,<br/>欢迎访问<a href="https://www.helloweba.net/">www.helloweba.net</a>',
                    'attach' => '/home/swoole/public/a.jpg'
                ];
                $this->sendMail($mailData);
                break;
            
            default:
                break;
        }
    }


    /**
    * @param $serv swoole_server swoole_server对象
    * @param $task_id int 任务id
    * @param $data string 任务返回的数据
    */
    public function onFinish(swoole_server $serv, $task_id, $data)
    {
        //
    }


    // 监听连接关闭事件
    public function onClose($serv, $fd, $from_id) {
        echo "Client {$fd} close connection\n";
    }

    public function stop()
    {
        $this->serv->stop();
    }

    private function sendMail($mail_data = [])
    {
        $mail = new PHPMailer(true);
        try {
            $mailConfig = $this->mailConfig;
            //$mail->SMTPDebug = 2;        // 启用Debug
            $mail->isSMTP();   // Set mailer to use SMTP
            $mail->Host = $mailConfig['smtp_server'];  // SMTP服务
            $mail->SMTPAuth = true;                  // Enable SMTP authentication
            $mail->Username = $mailConfig['username'];    // SMTP 用户名
            $mail->Password = $mailConfig['password'];     // SMTP 密码/口令
            $mail->SMTPSecure = $mailConfig['secure'];     // Enable TLS encryption, `ssl` also accepted
            $mail->Port = $mailConfig['port'];    // TCP 端口

            $mail->CharSet  = "UTF-8"; //字符集
            $mail->Encoding = "base64"; //编码方式

            //Recipients
            $mail->setFrom($mailConfig['username'], 'Helloweba'); //发件人地址,名称
            $mail->addAddress($mail_data['emailAddress'], '亲');     // 收件人地址和名称
            //$mail->addCC('[email protected]'); // 抄送

            //Attachments
            if (isset($mail_data['attach'])) {
                $mail->addAttachment($mail_data['attach']);         // 添加附件
            }
            
            //$mail->addAttachment('/tmp/image.jpg', 'new.jpg');    // Optional name

            //Content
            $mail->isHTML(true);                                  // Set email format to HTML
            $mail->Subject = $mail_data['subject'];
            $mail->Body    = $mail_data['body'];

            $mail->send();
            return true;
        } catch (\Exception $e) {
            echo 'Message could not be sent. Mailer Error: '. $mail->ErrorInfo;
            return false;
        }
    }

}

Swoole启动后,服务端会监听数据接收事件 onReceive() ,当接收到客户端发来的数据时会进行相应的处理。我们在这里对源数据进行验证,然后作为任务投递给 onTask()sendMail() 是使用phpmailler来发送邮件的,这里可以参考 使用PHPMailer发送带附件并支持HTML内容的邮件

运行服务端

在public/目录下建立mailServer.php,代码如下:

<?php 
require dirname(__DIR__) . '/vendor/autoload.php';

use Helloweba\Swoole\Mail;

$config = [
    'smtp_server' => 'smtp.163.com', //邮件服务器
    'username' => '[email protected]', //这里是用作发送方的邮箱号
    'password' => 'xxxxx',// SMTP 密码/口令
    'secure' => 'ssl', //Enable TLS encryption, `ssl` also accepted
    'port' => 465, // tcp邮件服务器端口
];
$server = new Mail($config);
$server->start();

你可以注册一个163邮箱,然后开通smtp功能。我DEMO中使用的是163邮箱发邮件发多了被封号了,所以在线演示demo没上传了。配置好邮件服务器参数后,运行:

php mailServer.php

此时再使用命令 netstat -lntp 查看进程:

iyUJFjV.jpg!web

当你看到已经在监听9502端口时,我们的swoole邮件服务端就已经启动好了。

运行客户端

在public/目录下新建mailClient.php,代码如下:

<?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 = 'sendMail';
        $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";
    }
}

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

运行命令启动客户端:

php mailClient.php

此时在命令行窗口会返回如下信息:

[root@localhost public]# php mailClient.php 
Get Message From Server:{"result":"success"}

这样就已经执行邮件发送任务了,如果出现故障,可以查看日志文件/data/logs/swoole.log。

小结

本节是一个简单的单一邮件发送例子。由Swoole客户端提交发送邮件的指令到Swoole服务端,服务端接收到需要发送邮件的指令后,调用邮件发送服务来发邮件的。由于发送邮件可能比较耗时,不同的邮件服务器发送耗时也不一样,经测试单个邮件发送一般在1秒内完成,这个如果在web页面上发送是可以接受的,但是使用Swoole来发送的话我们提交发送指令就不用管结果了,Swoole在后台自己进行发送邮件。如果是大批量发送大量邮件时,我们可以使用Swoole+队列的方案,下节我们来测试批量队列发送邮件,敬请关注。


Recommend

  • 44

    Swoole实验室:1-使用Composer构建项目 2018年09月01日 21:11 helloweba.net 作者:月光光 标签:PHP 

  • 34

    在上一节中我们介绍了使用Swoole发送单个邮件,那么如果是大量的邮件需要发送,比如给2万个用户发送优惠活动邮件,这是一个比较耗时的过程,而PHP本身不适合处理这种耗时多任务场景。本节为给大家介绍使用Swoole+Redis来实现发送批量邮件...

  • 103

    在线web聊天功能是基于WebSocket协议实现的,Swoole从1.7.9开始就增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器。今天我给大家讲解如何使用Swoole实现一个简易的聊天功能。

  • 47

    Swoole实验室:5-使用Systemctl管理Swoole服务 2018年10月17日 20:19 helloweba.net 作者:月光光 标签:PHP 

  • 45

    项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是swoole提供的强大的毫秒定时器。 应...

  • 3
    • yaoguais.github.io 1 year ago
    • Cache

    Swoole源码分析之数据接收与发送

    Swoole源码分析之数据接收与发送 process模式 base模式 事情的起因是这样的,进行Swoole应用开发的时候,发现各个Worker可以给任意一个客户端发送消息,即调用$serv->send($fd, $data), 而不用关心客户端第一次是哪个W...

  • 120
    • 掘金 juejin.im 6 years ago
    • Cache

    node.js使用Nodemailer发送邮件

    原文链接:Node.js使用Nodemailer发送邮件 电子邮件是—种用电子手段提供信息交换的通信方式,是互联网应用最广的服务。通过网络的电子邮件系统,用户可以以非常低廉的价格(不管发送到哪里,都只需负担网费)、非常快速的方式(几秒钟之内可以发送到世界上任何

  • 25

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封装。本文对python使用smtplib发送邮件做一...

  • 9
    • 微信 mp.weixin.qq.com 3 years ago
    • Cache

    Golang 语言怎么使用 gomail 库发送邮件?

    01 介绍 我们在项目开发中,经常会遇到发送邮件的需求,比如给开发人员发送报警邮件,给用户发送优惠券或兑换码等。 Gomail 是发送电子邮件的简单高效的包。它已经过充分测试和记录。Gomail 只能使用 SMTP 服务器发送电子邮件。但是...

  • 5
    • www.cnblogs.com 3 years ago
    • Cache

    使用 MimeKit 和 MailKit 发送邮件

    使用 MimeKit 和 MailKit 发送邮件 Mim...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK