1. 队列使用场景
1.1. 允许延后|异步|并行处理 (相对于传统的 即时|同步|串行 的执行方式)
- 允许延后:抢购活动时,先快速缓冲有限的参与人数到消息队列,后续再排队处理实际的抢购业务;
- 允许异步:业务处理过程中的邮件,短信等通知
- 允许并行:用户支付成功之后,邮件通知,微信通知,短信通知可以由多个不同的消费者并行执行,通知到达的时间不要求先后顺序。
1.2. 允许失败和重试
- 强一致性的业务放入核心流程处理
- 无一致性要求或最终一致即可的业务放入队列处理
2. php-resque介绍
php-resque 是轻量级后台任务系统,基于Redis,功能设计简单,配置灵活。相比MQ系统大而全的MQ系统,这个显得小而美
3. php-resque 角色划分
- Job 定义任务,是负责具体的业务逻辑。
- Queue 队列,负责Job存/取
- Worker 从Queue中取Job来执行。 一般为PHP CLI模式下,后台守护方式运行。
4. 场景使用示例
4.1. 场景业务介绍
定点开放约车业务场景中,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列,使任务逐个执行。
4.2. 队列使用流程图
4.3. 环境
- php 5.6
- thinkphp 3.2.3
- redis 3.2
5. 重要代码示例
5.1.1. 项目目录结构示例1
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
5.1.3. 生产者发送请求入队核心
php示例代码 : IndexController.class.php
_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
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. 运行演示
先启动Redis服务
windows下启动队列服务 下图所示
- 客户端模拟请求入队(生产者入队)
- 队列服务端打印生产者入队信息及队列处理信息 下图所示
- 客户端轮询请求获取队列处理信息
- 登入redis客户端查看信息
项目地址: https://github.com/xiao-xiangyeyu/thinkphp3.2.3-php-resque