1. 隊列使用場景
1.1. 允許延后|異步|并行處理 (相對于傳統(tǒng)的 即時|同步|串行 的執(zhí)行方式)
- 允許延后:搶購活動時绒极,先快速緩沖有限的參與人數(shù)到消息隊列赴肚,后續(xù)再排隊處理實際的搶購業(yè)務(wù)届氢;
- 允許異步:業(yè)務(wù)處理過程中的郵件乖菱,短信等通知
- 允許并行:用戶支付成功之后助币,郵件通知静陈,微信通知燕雁,短信通知可以由多個不同的消費者并行執(zhí)行榆鼠,通知到達的時間不要求先后順序挤庇。
1.2. 允許失敗和重試
- 強一致性的業(yè)務(wù)放入核心流程處理
- 無一致性要求或最終一致即可的業(yè)務(wù)放入隊列處理
2. php-resque介紹
php-resque 是輕量級后臺任務(wù)系統(tǒng),基于Redis俭缓,功能設(shè)計簡單刑赶,配置靈活捏浊。相比MQ系統(tǒng)大而全的MQ系統(tǒng),這個顯得小而美
3. php-resque 角色劃分
- Job 定義任務(wù)撞叨,是負責具體的業(yè)務(wù)邏輯金踪。
- Queue 隊列浊洞,負責Job存/取
- Worker 從Queue中取Job來執(zhí)行。 一般為PHP CLI模式下胡岔,后臺守護方式運行法希。
4. 場景使用示例
4.1. 場景業(yè)務(wù)介紹
定點開放約車業(yè)務(wù)場景中,一般會因為流量過大,導(dǎo)致流量暴增靶瘸,應(yīng)用掛掉铁材。為解決這個問題,一般需要在應(yīng)用前端加入消息隊列奕锌,使任務(wù)逐個執(zhí)行著觉。
4.2. 隊列使用流程圖
4.3. 環(huán)境
- php 5.6
- thinkphp 3.2.3
- redis 3.2
5. 重要代碼示例
5.1.1. 項目目錄結(jié)構(gòu)示例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
<?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. 生產(chǎn)者發(fā)送請求入隊核心
php示例代碼 : IndexController.class.php
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends BaseController {
/**
* [Joinbook 請求約車接口]
* 1.客戶端APP發(fā)送業(yè)務(wù)請求
* 2.該接口簡單校驗加入任務(wù)隊列
* 3.入隊成功返回入隊成功標識 jobId
*/
public function Joinbook()
{
$rid = I('rid',32); // 業(yè)務(wù)邏輯參數(shù)
$subject = I('subject',24); // 業(yè)務(wù)邏輯參數(shù)
$student_id = I('student_id',343820); // 業(yè)務(wù)邏輯參數(shù)
try{
if(empty($rid)|| empty($subject))
return $this->_return(0,"參數(shù)有誤");
$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);
// 入隊成功標識,客戶端使用此標識定時請求,隊列狀態(tài)查詢接口
$args['jobId'] = $jobId;
S($key,$args ,5); // 60秒內(nèi)禁止重復(fù)預(yù)約
$this->ajaxReturn(['msg'=>'入隊,預(yù)約成功','data'=>$args]);
}catch (\Exception $e){
$this->ajaxReturn(['異常']);
}
}
//隊列狀態(tài)查詢接口
public function JoinStatus()
{
$jobid = I("jobid");
$status = new \Resque\Job\Status($jobid);
//執(zhí)行完成告訴用戶是否成功
if (!$status->isTracking()) {
$this->_return(0,"不存在的排隊");
}else{
// 緩存key
$jobid = "Applet\Controller\V2\BooktrainControllerJoinbook_job".$jobid;
$info = S($jobid);
//隊列沒執(zhí)行
if(!$info){
$info = [];
$info['msg'] = "等待中...";
$info['status'] = 100; // 收到該結(jié)果 前端繼續(xù)輪詢,一般限制次數(shù)輪詢
$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
);
// 數(shù)據(jù)庫業(yè)務(wù)邏輯處理-start
// $deal_info = D('User')->deal($args);
$deal_info=[];
$status =$deal_info['status']?:'200';
$msg =$deal_info['msg']?:'預(yù)約成功';
// 數(shù)據(jù)庫業(yè)務(wù)邏輯處理-end
$margs = array(
'student_id'=>$args['student_id'] ,
'rid'=>$args['rid'] ,
'subject'=>$args['subject'],
'date'=>date('Y-m-d')
);
// 終端打印參數(shù)
fwrite(STDOUT,json_encode($args)."\n");
// 獲取當前業(yè)務(wù)的緩存參數(shù),app請求入隊時寫入
$key = "requse".md5(json_encode($margs));
$result = S($key);
//將業(yè)務(wù)邏輯處理結(jié)果
$result["status"] = $status;
$result["msg"] = $msg;
// 將參數(shù)及約車結(jié)果放入緩存
S($key,$result ,1200);
// 將約車的結(jié)果以工作任務(wù)標識 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 啰嗦模式啟動,會打印詳細調(diào)試信息
# --interval=2 在隊列中循環(huán)的間隔時間,即完成一個任務(wù)后的等待時間惊暴,默認是5秒
# --count=5 需要創(chuàng)建的Worker的進程數(shù)量
# --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
實際應(yīng)用部署過程中注意以守護進程方式啟動
5.1.6. 運行演示
先啟動Redis服務(wù)
windows下啟動隊列服務(wù) 下圖所示
- 客戶端模擬請求入隊(生產(chǎn)者入隊)
- 隊列服務(wù)端打印生產(chǎn)者入隊信息及隊列處理信息 下圖所示
- 客戶端輪詢請求獲取隊列處理信息
- 登入redis客戶端查看信息
項目地址: https://github.com/xiao-xiangyeyu/thinkphp3.2.3-php-resque