think-queue 消息队列 + redirs + Supervisor进程管理工具
前言
此类文章已经很多。就不累赘了。 这篇文章[hinkphp-queue](https://github.com/coolseven/notes/tree/master/thinkphp-queue)就已经很详细了,本文介绍在实战中的运用。提示:以下是本篇文章正文内容,下面案例可供参考
一、Supervisor是什么?
Supervisor 是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。也就是监控进程的状态。这篇文章Supervisor使用详解已经介绍很清楚了。
二、使用场景
1. 教育机构对结课学员课时,定时清零。
这个场景一般用的模式定时模式。已tp6为例。具体做法如下:
生产端 我们利用tp command创建自定义指令,查看创建自定义指令详解。
配置和执行控制类
namespace app\command;
use app\common\business\queue\ThinkQueueStuCourseBusiness;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\Output;
class ClassEnding extends Command
{
protected function configure()
{
// 指令配置
$this->setName('classend')
->setDescription('结课处理');
}
protected function execute(Input $input, Output $output)
{
// 指令输出
$output->writeln('============开始==============');
$this->check();
$output->writeln('============结束==============');
}
protected function check()
{
ThinkQueueStuCourseBusiness::getStuCourseList();
return;
}
}
业务逻辑类 hourClear方法就是把数据推送到队列中。
namespace app\common\business\queue;
use app\common\lib\queue\ThinkQueue;
use app\model\VendorMember;
use think\console\Output;
class ThinkQueueStuCourseBusiness
{
/**
* 获取列表
*/
public static function getStuCourseList()
{
$list = (new VendorMember())->field('vendor_member_id,goods_status,is_del')->whereDay('student_expire_time')
->where('is_del','=',0)
->where('goods_status','<>',1)
->select();
foreach ($list as $key => $value) {
self::hourClear($value);
$date = date("Y-m-d H:i:s");
(new Output)->writeln($date.' '."生产者:".$value['vendor_member_id']);
}
return true;
}
/**
* 课时清零
*/
public static function hourClear($jobData)
{
$jobData['method'] = 'fire';
$jobHandlerClassName = 'app\common\queue\HourClear';
$jobQueueName = 'hourClear';
$push = ThinkQueue::actionJob($jobData,$jobHandlerClassName,$jobQueueName);
return $push;
}
}
在console.php中添加配置
return [
// 指令定义
'commands' => [
'CreateModels' => 'app\command\CreateModels',
'classend' => 'app\command\ClassEnding',
],
];
在queue.php中添加配置
return [
'default' => 'redis',
'connections' => [
'sync' => [
'type' => 'sync',
],
'database' => [
'type' => 'database',
'queue' => 'default',
'table' => 'jobs',
'connection' => null,
],
'redis' => [
'type' => 'redis',
'queue' => 'default',
'host' => '127.0.0.1',
'port' => 6379,
'password' => '',
'select' => 0,
'timeout' => 0,
'persistent' => false,
],
],
'failed' => [
'type' => 'none',
'table' => 'failed_jobs',
],
];
run PHP Built-in Server for ThinkPHP
version show thinkphp framework version
make
make:command Create a new command class
make:controller Create a new resource controller class
make:event Create a new event class
make:listener Create a new listener class
make:middleware Create a new middleware class
make:model Create a new model class
make:service Create a new Service class
make:subscribe Create a new subscribe class
make:validate Create a validate class
optimize
optimize:route Build app route cache.
optimize:schema Build database schema cache.
queue
queue:failed List all of the failed queue jobs
queue:failed-table Create a migration for the failed queue jobs database table
queue:flush Flush all of the failed queue jobs
queue:forget Delete a failed queue job
queue:listen Listen to a given queue
queue:restart Restart queue worker daemons after their current job
消费端 是处理队列中的数据。处理成功就删除队列中的数据。直到清零为止。
<?php
namespace app\common\queue;
use app\api\business\StudentCourseBusiness;
use think\facade\Log;
use think\queue\Job;
use think\console\Output;
class HourClear
{
/**
* fire方法是消息队列默认调用的方法
* @param Job $job 当前的任务对象
* @param array|mixed $data 发布任务时自定义的数据
*/
public function fire(Job $job,$data)
{
// 有些消息在到达消费者时,可能已经不再需要执行了
$isJobDone = $this->doHelloJob($data);
if ($isJobDone) {
$job->delete();
//
Log::write(date('Y-m-d H:i:s').':'."<info>Hello Job has been done and deleted"."</info>",'info');
} else {
Log::write(date('Y-m-d H:i:s').':'."<info>失败"."</info>",'error');
if ($job->attempts() > 3) {
//通过这个方法可以检查这个任务已经重试了几次了
Log::write(date('Y-m-d H:i:s').':'."<warn>Hello Job has been retried more than 3 times!"."</warn>",'info');
$job->delete();
// 也可以重新发布这个任务
//print("<info>Hello Job will be availabe again after 2s."."</info>\n");
//$job->release(2); //$delay为延迟时间,表示该任务延迟2秒后再执行
}
}
}
/**
* 该方法用于接收任务执行失败的通知,你可以发送邮件给相应的负责人员
* @param $jobData string|array|... //发布任务时传递的 jobData 数据
*/
public function failed($jobData){
print("Warning: Job failed after max retries. job data is");
}
/**
* 有些消息在到达消费者时,可能已经不再需要执行了
* @param array|mixed $data 发布任务时自定义的数据
* @return boolean 任务执行的结果
*/
private function checkDatabaseToSeeIfJobNeedToBeDone($data){
return true;
}
/**
* 根据消息中的数据进行实际的业务处理...
*/
private function doHelloJob($data)
{
Log::write(date('Y-m-d H:i:s').':'.json_encode($data),'info');
$vendor_member_id = isset($data['vendor_member_id']) ? $data['vendor_member_id'] : "";
$save = (new StudentCourseBusiness())->edit(
[
'vendor_member_id' => $vendor_member_id,
'goods_status' => 1,
'student_remain_hours' => 0,
'vendor_edit_time' => time()
]
);
if ($save !== true) {
return false;
}
$date = date('Y-m-d H:i:s');
(new Output)->writeln($date.' '."消费者:".$data['vendor_member_id']);
return true;
}
}
服务端 主要是使用Supervisor进程来管理进程。能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启
提示:每天凌晨3执行,把数据推送到队列中。之后用Supervisor进行管理。
[root@localhost ~]# crontab -l
0 3 * * * /usr/bin/php /var/www/tp/think classend
[root@localhost supervisord.d]# pwd
/etc/supervisord.d
[root@localhost supervisord.d]# cat queue.ini
[program:queue]
directory = /var/www/tp
command = /usr/bin/php /var/www/tp/think queue:work --queue hourClear
process_name=%(program_name)s_%(process_num)02d
numprocs = 3
autostart = true
startsecs = 5
autorestart = true
startretries = 3
user = root
redirect_stderr = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups = 20
[root@localhost supervisord.d]#
2. 订单下单之后超过30分钟用户未支付,需要取消订单
这个场景需要,只需要延迟进入入列就可以。超过 30分钟就执行。如果30分钟内付款。在消费类查询,是否付款。如果付款,直接把队列删除,否则就取消订单。相似逻辑处理。
- 在生产者业务代码中
// 即时执行
$isPushed = Queue::push($jobHandlerClassName, $jobDataArr, $jobQueueName);
// 延迟 2 秒执行
$isPushed = Queue::later( 2, $jobHandlerClassName, $jobDataArr, $jobQueueName);
// 延迟到 2017-02-18 01:01:01 时刻执行
$time2wait = strtotime('2017-02-18 01:01:01') - strtotime('now');
$isPushed = Queue::later($time2wait,$jobHandlerClassName, $jobDataArr, $jobQueueName);
- :在消费者类中:
// 重发,即时执行
$job->release();
// 重发,延迟 2 秒执行
$job->release(2);
// 延迟到 2017-02-18 01:01:01 时刻执行
$time2wait = strtotime('2017-02-18 01:01:01') - strtotime('now');
$job->release($time2wait);
总结
希望这篇文章对大家有些帮助。本人经验有限,有描写不对的,或其他更好的方法。欢迎讨论。