hyperlau 发表于 2017-7-3 15:56:42

不规矩的异步邮件队列处理插件

本帖最后由 hyperlau 于 2017-7-3 16:02 编辑

不管是用CI的email类还是其他类,只要在php里调用smtp发邮件都会碰到需要等待发送完成才能返回的情况,用户体验非常不好,不爽,不利索。。。。
还好会perl,改了改N年前写的发邮件脚本,在php里写一条text到一个邮件队列的文本文件,然后crontab调用perl脚本来处理邮件队列,发送完成直接删掉这行,然后写日志到一个日志文本文件,没有等待,没有延迟,多开心~~
代码如下:
PHP部分:

<?php
class Msg {
      protected $CI;
      public function __construct()
      {
                $this->CI =& get_instance();
      }

      public function send_mail_q($to,$sub,$msg) {
                $this->CI->load->helper('file');
                $line='to:'.$to.'<end>sub:'.$sub.'<end>msg:'.$msg.'<end>'."\n";
                write_file('/tmp/mail_q', $line,"a");
      }

}
?>


Perl部分:

#!/usr/bin/perl -w
use strict;
use warnings;
use Tie::File;
use File::Path qw( mkpath );
use Time::Local;
use Net::SMTP;
use Authen::SASL;


$|=1;

my $mail_q='/tmp/mail_q';
my $log='/tmp/mail_log';
our $MailDebug = 0;
our $MailTimeout = 30;
our $MailUser = 'abc@abc.com';
our $MailPass = 'passwod';
our $MailHost = 'smtp.exmail.qq.com';
our $MailFrom = 'abc@abc.com';
our @MailList = ();

$SIG{'INT'}=sub {print "Script is running!!\n"};

unless (-e $mail_q) {
      dielogger($log,"can't found $_!");
}

my $result;
tie (my @lines,'Tie::File',$mail_q) || dielogger($log,"can't tie file to \@lines !SYSTEM_MSG: $!");
for (my $i=0;$i< @lines;$i++) {
      if ($lines[$i]=~/to:(.*)\<end\>sub:(.*)\<end\>msg:(.*)\<end\>$/) {
                $result=mail_send($2,$3,$1);
                if ($result==0) {
                        $lines[$i].=" SEND OK\n";
                        logger($log,$lines[$i]);
                        delete $lines[$i];
                }
      }
}

sub mail_send {
      my ($MailSubject, $MailContent,$MailTo) = @_;
      my $smtp = Net::SMTP->new($MailHost, Timeout=>$MailTimeout, Debug=>$MailDebug);
      if(!$smtp) {
                dielogger($log,"Can not connect to the mail server!");
                return 1;
      } else {
                my $auth_stat = $smtp->auth($MailUser, $MailPass);
                if (!$auth_stat) {
                        dielogger($log,"Mail account authentification failed!");
                        return 2;
                } else {
                        $smtp->mail($MailFrom);
                        $smtp->to($MailTo);
                        $smtp->data();
                        $smtp->datasend("Content-Type: text/html; charset=UTF-8\n");
                        $smtp->datasend("From:" . $MailFrom . "\n");
                        $smtp->datasend("To:" . $MailTo . "\n");
                        $smtp->datasend("Subject:" . $MailSubject . "\n");
                        $smtp->datasend("\n");
                        $smtp->datasend($MailContent);
                        $smtp->dataend();
                }
                $smtp->quit();
      }
      return 0;
}

sub logger
{
      my ($f,$m)=@_;
      open(LOGF,">>",$f)|| die "Failed to open $f.[\033\n";
      my $msg=get_time() . " $m\n";
      print $msg;
      print LOGF $msg;
      close LOGF;
}

sub dielogger
{
      my ($f,$m)=@_;
      logger($f,$m . " ##############QUIT##############\n");
      exit 1
}

sub get_time {
      my @time_format = (localtime());
      $time_format += 1900;
      $time_format += 1;
      foreach $_(@time_format) {
                $_ = sprintf("%02d", $_);
      }
      my $cur_time = "$time_format-$time_format-$time_format $time_format:$time_format:$time_format";
      return($cur_time);
}


Perl代码可以保存在/usr/local/nginx/html/app/third_party/q_mail.pl , 要注意权限,因为大部分web服务器都运行在一个牢笼用户下,要保证脚本可读可执行运行前需要安装所需的库,在shell下执行:

cpan Tie::File
cpan Net::SMTP
cpan Authen::SASL

crontab: ( 一分钟执行一次)

* * * * * /usr/local/nginx/html/app/third_party/q_mail.pl &

整合到自己的代码里,可以这样调用:

$this->load->library('msg');
$this->msg->send_mail_q('def@def.com','标题','内容');


完成!

页: [1]
查看完整版本: 不规矩的异步邮件队列处理插件