前些天在写我的TR-System(社团招新管理系统)的时候,用到了邮箱验证用户注册,也就是注册成功后,需要访问收到的邮件中的URL进行用户激活。这个事件发生在用户提交注册请求之后,系统需要立即发送一封邮件用于激活,但是系统在调用PHPMailer的时候需要进行SMTP连接邮箱系统(用的腾讯企业邮),往往在这个时候会出现等待的情况。如果让用户来承担这个等待时间,体验也太差了!,没见过哪个系统上注册个用户,还需要慢慢等待系统告诉我到底有没有注册成功。
一开始在写的时候,直接在POST请求中处理,写入数据库后就调用sendEmail方法,情况是这样的:
1.提交注册 --> 2.写入数据库 --> 3.发送Email --> 4.注册成功。
这样在用户看来是有一个漫长的等待的。
实际上应该为:
1.提交注册 --> 2.写入数据库 --> 3.注册成功。 ||||||||||||||||||-->发送Email
这样的才是一个拥有良好用户体验的系统 ^_^ 。
若是在Java等环境下,咱们可以很好利用多线程,分出来一个任务让另一个线程去完成。
但是!PHP这个单线程环境下,没有new Thread来搞这个事情啊。 不过,换一个角度,线程不行的,我就给个新进程呗。当然不是在当前进程创建新进程。

咱们重新写一个文件,sendEmail.php

$input = file_get_contents("php://input");
            $data = json_decode($input,true);
            $signature = $data['signature'];
            unset($data['signature']);
            ksort($data);
            $sign = sha1(implode($data));
            if($sign == $signature){
                SendMail($data['address'],$data['title'], $data['message'], $data['fromname']);
            }
通过代码可以看到,主要发送邮件的还是SendMail()方法,上面是做了一个验证算法的,这样是为了避免这个接口被他人恶意调用,当然,这只是很low的一个算法。 假如调用这个接口的URI为http://localhost/sendEmail.php 咱们在系统中写两个方法:
//异步调用方法
function SendMail_Sock($address, $title, $message, $fromname = '滴水网')
{
    $post_url = 'http://localhost/sendEmail.php';
    $arr=array(
            'address'=>$address,
            'title'=>$title,
            'message'=>$message,
            'fromname'=>$fromname,
    );
    ksort($arr);
    $arrstr = implode($arr);
    $arrstr = sha1($arrstr);
    $arr['signature'] = $arrstr;

    return sock_post($post_url,$arr);
}

//fsockopen模拟POST请求到指定url
function sock_post($url,$data=array()){
    // $query = http_build_query($data);
    $query = json_encode($data);
    $info = parse_url($url);
    $fp = fsockopen($info["host"], 80, $errno, $errstr, 8);
    $head = "POST ".$info['path']."?".$info["query"]." HTTP/1.0\r\n";
    $head .= "Host: ".$info['host']."\r\n";
    $head .= "Referer: http://".$info['host'].$info['path']."\r\n";
    $head .= "Content-type: application/x-www-form-urlencoded\r\n";
    $head .= "Content-Length: ".strlen(trim($query))."\r\n";
    $head .= "\r\n";
    $head .= trim($query);
    $write = fputs($fp, $head);
    return $write;    //只要fsockopen()成功打开,就返回成功,这里发送数据后并不需要接收反馈,所以无需等待
}

以后在需要异步发送邮件的时候直接使用SendMail_Sock()方法就行了。但是这样也有不利之处,那就是到底有没有发送成功,只有用户自己知道 【悲哀】。