php队列使用之php-resque

1. 队列使用场景

1.1. 允许延后|异步|并行处理 (相对于传统的 即时|同步|串行 的执行方式)

  1. 允许延后:抢购活动时,先快速缓冲有限的参与人数到消息队列,后续再排队处理实际的抢购业务;
  2. 允许异步:业务处理过程中的邮件,短信等通知
  3. 允许并行:用户支付成功之后,邮件通知,微信通知,短信通知可以由多个不同的消费者并行执行,通知到达的时间不要求先后顺序。

1.2. 允许失败和重试

  1. 强一致性的业务放入核心流程处理
  2. 无一致性要求或最终一致即可的业务放入队列处理

2. php-resque介绍

php-resque 是轻量级后台任务系统,基于Redis,功能设计简单,配置灵活。相比MQ系统大而全的MQ系统,这个显得小而美

3. php-resque 角色划分

  1. Job 定义任务,是负责具体的业务逻辑。
  2. Queue 队列,负责Job存/取
  3. Worker 从Queue中取Job来执行。 一般为PHP CLI模式下,后台守护方式运行。

4. 场景使用示例

4.1. 场景业务介绍

定点开放约车业务场景中,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列,使任务逐个执行。

4.2. 队列使用流程图

队列使用流程.png

4.3. 环境

  1. php 5.6
  2. thinkphp 3.2.3
  3. redis 3.2

5. 重要代码示例

5.1.1. 项目目录结构示例1

项目结构示例.png
项目结构示例.png

5.1. 核心代码示例

5.1.1. Redis配置

php示例代码: config.php

'QUEUE' => array(
        'type' => 'redis',
        'host' => '127.0.0.1',
        'port' =>  '6379',
        'prefix' => 'queues',
        'auth' =>  '',
    ),

5.1.2. 队列单入口配置

php示例代码: resque

#!/usr/bin/env php
<?php
ini_set('display_errors', true);
error_reporting(E_ERROR);
define('APP_PATH','./Application/');
define('MODE_NAME', 'cli');
define('BIND_MODULE', 'Home');
define('BIND_CONTROLLER', 'Queue');
define('BIND_ACTION', 'index');
$act = $argv[1] ? $argv[1] : 'start';
putenv("Q_ACTION={$act}");
putenv("Q_ARGV=" . json_encode($argv));
require './ThinkPHP/ThinkPHP.php';

5.1.3. 生产者发送请求入队核心

php示例代码 : IndexController.class.php

<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends BaseController {
    /**
     * [Joinbook 请求约车接口]
     *  1.客户端APP发送业务请求
     *  2.该接口简单校验加入任务队列
     *  3.入队成功返回入队成功标识 jobId
     */
    public function Joinbook()
    {
        $rid = I('rid',32); // 业务逻辑参数
        $subject = I('subject',24); // 业务逻辑参数
        $student_id = I('student_id',343820); // 业务逻辑参数
        try{
            if(empty($rid)|| empty($subject))
                return $this->_return(0,"参数有误");
            $args = array(
                'student_id'=>$student_id , 
                'rid'=>$rid ,
                'subject'=>$subject,
                'date'=>date('Y-m-d')
            );
            // 缓存key
            $key = "requse".md5(json_encode($args));
            $yueyue = S($key);
            if($yueyue){
                $this->ajaxReturn(['排队中,请稍后']);
            }
            $jobId = \Resque::enqueue('default', \Common\Job\Job::Booktrain, $args, true);
            // 入队成功标识,客户端使用此标识定时请求,队列状态查询接口
            $args['jobId'] = $jobId;
            S($key,$args ,5); // 60秒内禁止重复预约
            $this->ajaxReturn(['msg'=>'入队,预约成功','data'=>$args]);
        }catch (\Exception $e){
            $this->ajaxReturn(['异常']);
        }
    }
    //队列状态查询接口
    public function JoinStatus()
    {
        $jobid = I("jobid");
        $status = new \Resque\Job\Status($jobid);
        //执行完成告诉用户是否成功
        if (!$status->isTracking()) {
            $this->_return(0,"不存在的排队");
        }else{
            // 缓存key
            $jobid = "Applet\Controller\V2\BooktrainControllerJoinbook_job".$jobid;
            $info = S($jobid);
            //队列没执行
            if(!$info){
                $info = [];
                $info['msg'] = "等待中...";
                $info['status'] =   100; // 收到该结果 前端继续轮询,一般限制次数轮询
                $this->ajaxReturn($info);
            }
            $this->ajaxReturn($info);  
        }
    }
}

5.1.4. 消费者核心代码

php示例代码 : BooktrainJob.php

<?php
namespace Common\Job;
class BooktrainJob
{
    public function perform()
    {
        $args = $this->args;
        $rid = $args['rid'];
        $subject = $args['subject'];
        $args = array(
            'student_id'=>$args['student_id'] , 
            'rid'=>$rid ,
            'subject'=>$subject
        );
        // 数据库业务逻辑处理-start
        // $deal_info = D('User')->deal($args);
        $deal_info=[];
        $status =$deal_info['status']?:'200';
        $msg =$deal_info['msg']?:'预约成功';
        // 数据库业务逻辑处理-end
        $margs = array(
            'student_id'=>$args['student_id'] , 
            'rid'=>$args['rid'] ,
            'subject'=>$args['subject'],
            'date'=>date('Y-m-d')
        );
        // 终端打印参数
        fwrite(STDOUT,json_encode($args)."\n");

        // 获取当前业务的缓存参数,app请求入队时写入
        $key = "requse".md5(json_encode($margs)); 
        $result = S($key);

        //将业务逻辑处理结果
        $result["status"] = $status;
        $result["msg"] = $msg;

        // 将参数及约车结果放入缓存
        S($key,$result ,1200); 
        
        // 将约车的结果以工作任务标识 jobID为key放入缓存,等待客户端轮询获取
        $jobid = "Applet\Controller\V2\BooktrainControllerJoinbook_job".$result['jobId'];
        S($jobid,$result ,300);
    }
}

5.1.5. 命令详解

# 进入项目目录
cd /var/www/html/

# 启动队列
# --queue=default 启动队列的名称  default(队列名称)
# --debug=1  啰嗦模式启动,会打印详细调试信息
# --interval=2 在队列中循环的间隔时间,即完成一个任务后的等待时间,默认是5秒
# --count=5 需要创建的Worker的进程数量
# --pid=/tmp/resque.pid 手动指定PID文件的位置,适用于单Worker运行方式

php resque start --queue=default --debug=1 --interval=2 --count=5 --pid=/tmp/resque.pid

# 查看进程及杀死进程
ps aux | grep resque |awk ‘{print $2}’|xargs kill

实际应用部署过程中注意以守护进程方式启动

5.1.6. 运行演示

  1. 先启动Redis服务

  2. windows下启动队列服务 下图所示

windows下启动队列.png
  1. 客户端模拟请求入队(生产者入队)
客户端请求入队.png
  1. 队列服务端打印生产者入队信息及队列处理信息 下图所示
入队信息打印.png
  1. 客户端轮询请求获取队列处理信息
客户端轮询请求.png
  1. 登入redis客户端查看信息
redis客户端查看队列存储信息.png

项目地址: https://github.com/xiao-xiangyeyu/thinkphp3.2.3-php-resque

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容