一、队列使用场景:为什么需要队列
在web开发中,我们经常会遇到需要处理批量任务的时候,这些批量任务可能是用户提交的,也可能是当系统被某个事件触发时需要进行批量处理的,面对这样的 任务,如果是用户提交的批量任务,初级程序员只能让用户触发提交动作后,等待服务器处理完毕,并且将结果返回到浏览器,期间用户不能关掉浏览器窗口,如果 数据比较大,或者处理速度比较慢,那用户体验将会因此受到直接影响。但是当我们使用某讯或者某浪的邮箱时,点击群发邮件之后,只需等待很短的时间,浏览器 提示提交成功,正在发送之类的信息时,用户就可以关掉浏览器,稍后,收件地址栏里的邮箱将陆续收到该群发邮件,再比如群发定时邮件,以及当商城系统中有客 户下单,客户,客服,仓库等相关人员收到订单邮件信息。诸如此类,队列的应用范围是如此之广。

二 :普通工程师的解决方案和架构师的解决方案
方案1:建表存邮件,消息等,用定时程序取出发送。

方案2:抽象到更高一层,开发一套通用异步处理队列适用于任何复杂的业务逻辑
那么,作为架构师,使用队列的做法,将抽象层和业务层分离,可具有良好的扩展性和可维护性。相比较而言就高明了许多,下面就我们介绍一下自定义队列的实现思路和方法。

三 :队列总体设计

1:需要队列程序,提供加入队列接口和取队列接口等
2:需要存储队列,文件或者数据库
3:需要定时程序取出队列并执行
4:其它扩展功能:优先级,日志,定时等

代码的目录结构如下,每个文件的作用用//注释来标明
|--addTask.php //添加任务到队列的例子
|--cronMission.php //定时任务调度程序,例如linux中受crontab直接调用的文件,业务逻辑工程师可以在这个文件中灵活定义自己的队列任务,从而不用每个队列任务 都需要上服务器修改crontab,从而在安全性,便捷性方面有很大提高
|--db.php //数据库操作
|--db.sql //建立队列需要用到的基本表结构
|--doQueue.php //执行队列任务
|--Queue.class.php //队列核心业务在这里定义,包括将任务加入队列,读队列,更改队列任务状态
|--sendMsg.php //队列要实现具体任务的业务接口,比如现有系统的发送消息的接口,这里例子中因为将此队列程序和现有系统系统集成,用写入日志来演示

四 :队列具体实现一:建任务存储表
1:
先来个最基本的:

CREATE TABLE`queue` (
 id int(11) NOT NULL auto_increment primarykey,
 taskphp varchar(128) NOT NULL default '',
 param text not null default '',
 status tinyint not null default 0,
 ctime timestamp NOT NULL default CURRENT_TIMESTAMP,
 KEY (ctime)
) ENGINE=InnoDBDEFAULT CHARSET=utf8;

字段解释:
taskphp:处理业务的接口文件
param:处理业务的接口文件需要接收的参数
status:任务处理状态,0为未处理,处理完毕更改为1

五 、队列具体实现二:定义调用接口

写核心类Queue.class.php, 实现基本接口:
1:加入队列接口
l //$param1 为执行任务的程序,$param2 为程序参数,可以为序列化的数据
l $cqueue->add($param1,$param2);
2: 读取队列接口
l $tasks = $cqueue->getQueueTask($limit = 1000);
3:更新任务状态
l $cqueue->updateTaskStatus($id);
4:a2s是自定义的一个数组转换字符串方法,这里不要使用json_encode,容易出现问题,同样,从数据库中取出转换为数组的时候,使用s2a方法
l $re = $cqueue->add("sendMsg.php", Queue::a2s($arr));

 

  1 <?php
  2 /**
  3  * 优才网公开课示例代码
  4  *
  5  * 任务队列实现
  6  *
  7  * @author 优才网全栈工程师教研组  张友林
  8  * @see http://www.ucai.cn
  9  */
 10 
 11 include_once('db.php');
 12 class Queue
 13 {
 14 
 15     /**
 16      * 把任务扔到队列
 17      *
 18      * @param string $taskphp   执行任务的程序
 19      * @param string $param     执行任务程序所用的参数
 20 
 21      * 例如,群发消息加入队列:
 22      * $arr = array(
 23      *      "uid" => 4,//发信息的人的UID
 24      *      "uids" => array(6,234,34,67,7888,2355), //接收信息的人的UID
 25      *      "content" => 'xxxxx',//信息内容
 26      *  );
 27      * $cqueue = new Queue();
 28      * $cqueue->add("/app/send_msg.php", serialize($arr));
 29      *
 30      */
 31     public function add($taskphp,$param)
 32     {
 33         $taskphp = mysql_real_escape_string($taskphp);
 34         //$param = mysql_real_escape_string($param);
 35         $param = $param;
 36         $sql = "insert into queue (taskphp, param) values('".$taskphp."', '".$param."')";
 37         $re = execute($sql);
 38         if ($re)
 39         {
 40             $pid = mysql_insert_id();
 41             return $pid;
 42         }
 43         else
 44         {
 45             return false;
 46         }
 47     }
 48 
 49 
 50     /**
 51      * 读取任务队列
 52      * 
 53      * @param string $limit 一次取多少条
 54      */
 55      public function getQueueTask($limit = 1000)
 56      {
 57         $limit = (int)$limit;
 58         $sql = "select id, taskphp, param from queue  where status = 0 order by id asc";
 59         $re = query($sql);
 60         return $re;
 61      }
 62 
 63     /**
 64      * 更新任务状态
 65      * 
 66      * @param string $limit 一次取多少条
 67      */
 68      public function updateTaskByID($id)
 69      {
 70         $id = (int)$id;
 71         $mtime = time();
 72         $sql = "update queue  set status =1, mtime = ".$mtime." where id = ".$id;
 73         $re = execute($sql);
 74         return $re;
 75      }
 76 
 77 
 78      public static function a2s($arr)
 79     {
 80         $str = "";
 81         foreach ($arr as $key => $value)
 82         {
 83             if (is_array($value))
 84             {
 85                 foreach ($value as $value2)
 86                 {
 87                     $str .= urlencode($key) . "[]=" . urlencode($value2) . "&";
 88                 }
 89             }
 90             else
 91             {
 92                 $str .= urlencode($key) . "=" . urlencode($value) . "&";
 93             }
 94         }
 95         return $str;
 96     }
 97 
 98     public static function s2a($str)
 99     {
100         $arr = array();
101         parse_str($str, $arr);
102         return $arr;
103     }
104 
105 }
106 
107 ?>

接下来就是部署调度了, 这个呢也不是就配置crontab那么简单,要可维护性,可扩展性好,能多进程并发执行,能监控队列运行状态等等,太长了,这个下一篇讲。