thinkphp5 queue redis队列使用,原创可用

介绍:

thinkphp-queue 是thinkphp 官方提供的一个消息队列服务,它支持消息队列的一些基本特性:

  • 消息的发布,获取,执行,删除,重发,失败处理,延迟执行,超时控制等
  • 队列的多队列, 内存限制 ,启动,停止,守护等
  • 消息队列可降级为同步执行

thinkphp-queue 内置了 Redis,Database,Topthink ,Sync这四种驱动。本文主要介绍 thinkphp-queue 结合其内置的 redis 驱动的使用方式和基本原理。

使用目的是为了,异步执行,减少业务流程等待时间。
实际场景是业务中记录很多调试日志,为了尽量少的影响业务响应时间,先将写入日志的请求发到队列里,然后通过消费者异步消费写入日志。

安装thinkphp-queue:

composer install topthink/think-queue

目录结构:

tp5 按照配置redis thinkphp5 redis队列_队列

  • 配置文件:/application/extra/queue.php。
  • 消费者:/application/queue/job/DebugLog。
  • 生产者:/application/queue/push/PushQueue。

代码:

配置:/application/extra/queue.php

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
use \think\Env;
return [
//     'connector' => 'Sync',

    'connector'  => 'Redis',        // Redis 驱动
    'expire'     => 60,     // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
    'default'    => 'default',      // 默认的队列名称
    'host'       => Env::get('redis.REDIS_MASTER_HOST', '127.0.0.1'),    // redis 主机ip
    'port'       => Env::get('redis.REDIS_MASTER_PORT', '6379'),       // redis 端口
    'password'   => Env::get('redis.REDIS_MASTER_PASSWORD', 'ups123456ups'),     // redis 密码
    'select'     => 0,      // 使用哪一个 db,默认为 db0
    'timeout'    => 0,      // redis连接的超时时间
    'persistent' => false,      // 是否是长连接
];

配置文件中的 expire 参数说明:

expire 参数指的是任务的过期时间, 单位为秒。 过期的任务,其准确的定义是

任务的状态为执行中
任务的开始执行的时刻 + expire > 当前时刻
expire 不为null 时 ,thinkphp-queue 会在每次获取下一个任务之前检查并重发过期(执行超时)的任务。

expire 为null 时,thinkphp-queue 不会检查过期的任务,性能相对较高一点。但是需要注意:

这些执行超时的任务会一直留在消息队列中,需要开发者另行处理(删除或者重发)!

消费者:/application/queue/job/DebugLog.php(注:这里也可改成通用的消费者)

<?php
namespace app\queue\job;

use think\Cache;
use think\queue\Job;

/**
 * 消费者
 * 调试日志
 * Class DebugLog
 * 队列名:DebugLogQueue
 * 启动方式:php think queue:work --daemon --queue DebugLogQueue
 * @package app\queue\job
 */
class DebugLog
{
    /**
     * Explain: fire方法是消息队列默认调用的方法
     * @param Job $job 当前的任务对象
     * @param $data 发布任务时自定义的数据
     * User: smt \(^o^)/~
     * Date: 2020-10-14 15:12
     * php think queue:work --daemon --sleep  3 --tries  2 --delay 3 --queue debugLogJobQueue
     * php think queue:work --daemon --tries  2 --delay 3 --queue debugLogJobQueue
     * daemon:后台守护进程   helloJobQueue:队列名称 sleep:无任务休眠3秒 tries:执行次数上线,比如异常  delay:抛出异常时,延迟3秒在执行
     */
    public function fire(Job $job, $data)
    {
        if (empty($data)) return;
        $data = $data['content'];

        echo "in hand... \n";

        // 有些消息在到达消费者时,可能已经不再需要执行了
        $isJobStillNeedToBeDone = $this->checkDatabaseToSeeIfJobNeedToBeDone($data);
        if(!$isJobStillNeedToBeDone){
            $job->delete();
            return;
        }

        // 业务处理
        $isJobDone = $this->handleTask($data);

        if ($isJobDone) {
            // 如果任务执行成功, 记得删除任务
            $job->delete();
        }else{
            if ($job->attempts() > 3) {
                //通过这个方法可以检查这个任务已经重试了几次了
                logs($data,'debug_log_queue_error');

                // $job->delete();

                // 也可以重新发布这个任务
                $job->release(2); //$delay为延迟时间,表示该任务延迟2秒后再执行
            }
        }
        echo "ending \n";
    }

    /**
     * Explain: 有些消息在到达消费者时,可能已经不再需要执行了
     * @param $data 发布任务时自定义的数据
     * @return bool 任务执行的结果
     * User: smt \(^o^)/~
     * Date: 2020-10-14 15:15
     */
    private function checkDatabaseToSeeIfJobNeedToBeDone($data){
        return true;
    }

    /**
     * Explain: 根据消息中的数据进行实际的业务处理...
     * @param $data 发布任务时自定义的数据
     * @return bool
     * User: smt \(^o^)/~
     * Date: 2020-10-14 15:16
     */
    private function handleTask($data)
    {
        $res = \app\common\model\DebugLog::create($data);
        if (empty($res)){
            logs($data,'debug_log_queue_handleTask_error');
            return false;
        }
        return true;
    }
}

生产者:/application/queue/push/PushQueue.php

<?php
namespace app\queue\push;
use think\Queue;

/**
 * 生产者
 * 调试日志
 * Class DebugLog
 * @package app\queue\job
 */
class PushQueue
{
    /**
     * Explain: 说明
     * @param $data 发送到队列中的数据
     * @param $consumerJob 消费者任务文件名 DebugLog
     * @param $queueName 队列名称 debugLogJobQueue
     * User: smt \(^o^)/~
     * Date: 2020-10-14 16:08
     */
    public static function pushMsg($data,$consumerJob,$queueName){
        $consumerJob = "app\queue\job\\$consumerJob";
        $content = [
            'str'=>time().uniqid(),
            'content' => $data
        ];
        // 入队:队列类,队列数据参数,队列名称:用于执行队列命名
        $isPushed = Queue::push($consumerJob, $content, $queueName);
        // database 驱动时,返回值为 1|false  ;   redis 驱动时,返回值为 随机字符串|false
        if( $isPushed === false ){
            logs(['data'=>$content,'consumerJob'=>$consumerJob,'queueName'=>$queueName],'push_queue_error');
        }else{
            logs(['data'=>$content,'consumerJob'=>$consumerJob,'queueName'=>$queueName],'push_queue_ok');
        }
        return true;
    }
}

业务中使用:

1、先启动消费者进行监听消费:

// 启动消费者
$ php think queue:work --daemon --tries  2 --delay 3 --queue DebugLogQueue

附上linux docker环境启动方式:

[root@VM_0_2_centos ups_develop]# docker exec -t e8085f79d2cd /bin/bash -c "php /home/wwwroot/ups-dev/think queue:work --daemon --tries  2 --delay 3 --queue DebugLogQueue"

in hand... 
ending 
Processed: app\queue\job\DebugLog
in hand... 
ending 
Processed: app\queue\job\DebugLog
in hand... 
ending 
Processed: app\queue\job\DebugLog

linux环境想要退出监控预览、后台执行,直接 ctrl + c ;

2、发送任务到队列:

use app\queue\push\PushQueue;

// 发送到队列,data:消息、DebugLog:job消费者文件名、DebugLogQueue:队列名称
PushQueue::pushMsg($data,'DebugLog','DebugLogQueue');

命令模式介绍:

  • queue:work 命令
  • work 命令: 该命令将启动一个 work 进程来处理消息队列。
php think queue:work --queue DebugLogQueue
  • queue:listen 命令
listen 命令: 该命令将会启动一个 listen 进程 ,然后由 listen 进程通过 proc_open(‘php think queue:work --queue="%s" --delay=%s --memory=%s --sleep=%s --tries=%s’) 的方式来周期性地创建一次性的 work 进程来消费消息队列, 并且限制该 work 进程的执行时间, 同时通过管道来监听 work 进程的输出。

php think queue:listen --queue DebugLogQueue

命令行参数:

Work 模式

php think queue:work \
--daemon            //是否循环执行,如果不加该参数,则该命令处理完下一个消息就退出
--queue  helloJobQueue  //要处理的队列的名称
--delay  0 \        //如果本次任务执行抛出异常且任务未被删除时,设置其下次执行前延迟多少秒,默认为0
--force  \          //系统处于维护状态时是否仍然处理任务,并未找到相关说明
--memory 128 \      //该进程允许使用的内存上限,以 M 为单位
--sleep  3 \        //如果队列中无任务,则sleep多少秒后重新检查(work+daemon模式)或者退出(listen或非daemon模式)
--tries  2          //如果任务已经超过尝试次数上限,则触发‘任务尝试次数超限’事件,默认为0

Listen 模式

php think queue:listen \
--queue  helloJobQueue \   //监听的队列的名称
--delay  0 \         //如果本次任务执行抛出异常且任务未被删除时,设置其下次执行前延迟多少秒,默认为0
--memory 128 \       //该进程允许使用的内存上限,以 M 为单位
--sleep  3 \         //如果队列中无任务,则多长时间后重新检查
--tries  0 \         //如果任务已经超过重发次数上限,则进入失败处理逻辑,默认为0
--timeout 60         // work 进程允许执行的最长时间,以秒为单位

end