后台订单提醒引发的PHP队列问题 [基础教程]

作者:代号黑鹰 发布于:2016-09-03 浏览:1239次 收藏

一个月前吧要弄个后台提醒功能,我的做法就是大多数的人一样,后台布局页面轮询,然后查询数据库订单状态,这个是最简单的方法吧。下面是我的那些垃圾代码。

先看看效果图吧:(语音提醒外加订单处理页面跳转

hello.png

这里明显是视图里面的layout/main.php,前端代码如下:

这里有个30秒轮询的过程。

<body>

<audio src="<?=Url::to('@web/themes/quirk/neworder.wav')?>" type="audio/wav" id="newmsg"></audio>

</body>

<script>
    $(function(){
        setInterval(newOrder,30000);
        function newOrder(){
            var url ='<?=Url::to(['service-order/hint'])?>';
            $.getJSON(url,{type:'hint'},function(data){
               var HtmlHint='';
                if(data.new==1){
                   document.getElementById('newmsg').play();
                }
                $('#msgNum').text("订单("+data.num+")");
               for(var i=0;i<data.num;i++){
                   HtmlHint+= '<li class="list-group-item unread">'+
                       '<div class="row" >'+
                       '<div class="col-xs-2">'+
                       '<i class="fa fa-envelope"></i>'+
                       '</div><div class="col-xs-10">'+
                       '<h5><a href="/service-order/index?num='+data.order[i]['order_num']+'">'
                       +data.order[i]['service_name']+
                       '</a></h5><small>'+data.order[i]['order_num']+
                       '</small></div></div></li>';

               }
                $('#msgHint').append(HtmlHint);
            });
        }
    })
</script>

那么后台是怎么处理的呢?(下面get到的这个hint只是过滤一下请求罢了,没有什么意义)

/**
 * 订单提醒---老方法
 */
public function actionOldHint(){
   if(Yii::$app->request->isAjax){
      $type=Yii::$app->request->get('type');
      if($type=='hint'){
         $sql = "select id,order_num,service_name from {{%service_order}} WHERE order_state=0
          AND FROM_UNIXTIME(create_time,'%Y-%m-%d')=curdate()";
          
         $newOrder = Yii::$app->db->createCommand($sql)->queryAll();
         $result = Array();
         $result['num']=count($newOrder);
         $result['order']=$newOrder;
         $time = time()-35;
         $sql = "select id from {{%service_order}} WHERE order_state=0 AND create_time>$time";
         $msgOrder = Yii::$app->db->createCommand($sql)->queryAll();
         $result['new']=$msgOrder?1:0;//是否是新的订单
         if($newOrder){
            return json_encode($result);
         }
      }
   }
}

大家看到了,第一条sql语句是查询单天的未处理订单。第二条才是查询刚刚下的订单通过状态和时间判断两个条件。显然有很多不足之处。大家如果使用了这种方法的话,自行体验他的不足之处。


下面才是我的正点-----------PHP队列问题处理。这里要感谢酱油君的指导

用workman 是一款纯PHP开发开源高性能PHP socket 服务器框架

这个不适合我-我只是弄个订单提醒-用个框架这也太。。。。此时我内心崩溃了,但是想到当年大学时期老师讲的队列问题,也是很简单的啊。但是我写着就是实现不了呢?原来当年老师讲课我睡觉去了。

(首先要明白队列的几个要素)

  1. 要符合先进先出原则

  2. 要有持久化数据存储,(不管什么方式)

下面说说我这个问题怎么用的队列吧

最开始我是想定义一个单列,弄个全局变量来处理数据的队列,但是数组的array_push()操作老是报不支持对象,也许这个想法在Java里面行的通。好吧不是这个持久化数据存储吗?用mysql吧----等等好像不对我就是要减轻mysql数据库压力的还去用mysql做队列不是本末倒置吗?

好吧我想到了一个办法请看下面这个类。

<?php
/**
 * Created by PhpStorm.
 * User: HeiYing
 * Date: 2016/8/30
 * Time: 11:02
 */

namespace common\core;

class Queue
{
    private $data;        // 持久化数据存储的地

    public function __construct(){
        $this->data = dirname(__FILE__).'/queue.text';
    }

    /**(尾部)入队  **/
    public function addQueue($value)
    {
        $file = fopen($this->data,'a+') or die("Unable to open file!");
        $txt = $value."\r\n";
        $res = fwrite($file, $txt);
        fclose($file);
        return $res;
    }

    /**(头部)出队**/
    public function removeQueue()
    {
        $file = fopen($this->data, "r") or die("Unable to open file!");
        $buffer = fgets($file);
        ob_start();
        fpassthru($file);     //输出
        fclose($file);
        file_put_contents($this->data, ob_get_clean() );
        return $buffer;
    }

    /**清空队列**/
    public function makeEmpty()
    {
       fopen($this->data, "w+") or die("Unable to open file!");
    }

    /**获取列头**/
    public function getFirst()
    {
        $file = fopen($this->data, "r") or die("Unable to open file!");
        $buffer = fgets($file);
        fclose($file);
        return $buffer;
    }

    /** 获取列尾 **/
    public function getLast($num=1)
    {
        $fp = fopen($this->data,"r") or die("Unable to open file!");
        $pos = -2;
        $eof = "";
        $head = false;   //当总行数小于Num时,判断是否到第一行了
        while($num>0){
            while($eof != "\n"){
                if(fseek($fp, $pos, SEEK_END)==0){    //fseek成功返回0,失败返回-1
                    $eof = fgetc($fp);
                    $pos--;
                }else{                               //当到达第一行,行首时,设置$pos失败
                    fseek($fp,0,SEEK_SET);
                    $head = true;                   //到达文件头部,开关打开
                    break;
                }
            }
            $buffer =fgets($fp);
            if($head){ break; }                 //这一句,只能放上一句后,因为到文件头后,把第一行读取出来再跳出整个循环
            $eof = "";
            $num--;
        }
        fclose($fp);
        return $buffer;
    }

    /** 获取长度 **/
    public function getLength()
    {
        $fp=fopen($this->data, "r");
        $i=0;
        while(!feof($fp)) {
            //每次读取2M
            if($data=fread($fp,1024*1024*2)){
                //计算读取到的行数
                $num=substr_count($data,"\r\n");
                $i+=$num;
            }
        }
        fclose($fp);
        return $i;
    }
}

这段代码就是用一个text文本做数据的持久化存储。

现在回到订单提醒问题,前端页面的代码还是不变,现在改后台代码。

/**
 * 订单提醒--队列实现
 */
public function actionHint(){
   if(Yii::$app->request->isAjax){
      $type=Yii::$app->request->get('type');
      if($type=='hint'){
         $queue =new Queue();
         $result['num']=$queue->getLength();
         $result['new']=0;
         $order = Array();
         while($queue->getLength()){  //队列存在
            $temp = json_decode($queue->removeQueue());
            array_push($order,$temp);
            $result['new']=1;
         }
         $result['order']=$order;
         return json_encode($result);
      }
   }
}


现在是不是很清晰了呢?但是还是有问题,不过比之前的订单提醒功能好多了。


如果文章对您有所帮助,希望继续支持我们,您的支持是我们最大的动力 ¥打赏
标签: php队列 队列 后台订单提醒
声明:文章内容由作者原创或整理,未经允许,不得转载!
  • 评论于 2016-09-18 10:39:29

    真棒 看起来很不错

    回复
  • 评论于 2016-09-05 09:46:20

    就一个问题 多人的咋办 每个人创建一个txt? 我感觉还是得数据库存储 

    回复于 2016-09-17 00:18:30 回复
    可以使用nosql数据库代替比如mongodb
    回复于 2016-10-08 13:27:14 回复
    只是一种队列思想而已。
    回复
您需要登录后才可以评论。登录 | 立即注册