php实现redis延时队列

11-07 1,865 views

写了一个简单的类实现

使用方法

$class = new RedisDelayQueue();
$class->execute();
<?php

namespace app\common\service;

class RedisDelay
{
    protected $prefix = 'delay_queue:';
    protected $redis = null;
    protected $key = '';

    public function __construct()
    {
        $this->key = $this->prefix . $this->getDefaultKey();
        $this->redis = //这里使用predis 或者 redis扩展连接;
    }

    public function getDefaultKey()
    {
        return config('custom.server_redis_delay_key');
    }

    public function delTask($value)
    {
        return $this->redis->zRem($this->key, $value);
    }

    public function getTask()
    {
        //获取任务,以0和当前时间为区间,返回一条记录
        return $this->redis->zRangeByScore($this->key, 0, time(), ['limit' => [0, 1]]);
    }

    /**
     * @param array $data
     * 'class' => '\\app\\common\\jobs\\video\\VideoConvert',
     * 'params' => [
     * 'asset_id' => $asset_id
     * ]
     * @param int $time
     * @return mixed
     */
    public function addTask(array $data, int $time)
    {
        //添加任务,以时间作为score,对任务队列按时间从小到大排序
        return $this->redis->zAdd(
            $this->key,
            $time,
            json_encode($data)
        );
    }

    public function run()
    {
        //每次只取一条任务
        $task = $this->getTask();

        if (empty($task)) {
            return false;
        }

        $task = $task[0];
        //有并发的可能,这里通过zrem返回值判断谁抢到该任务
        if ($this->delTask($task)) {

            $info = is_null(json_decode($task)) ? [] : json_decode($task, TRUE);

            if (!empty($info)) {
                $class = new $info['class']($info['params']);
                $class->run();
            }

            //删除变量
            unset($info, $class);

            //处理任务
            return true;
        }

        return false;
    }
}

死循环

<?php
/**
 * Redis延时队列
 */

namespace app\common\command;

use app\common\lib\log\Log;
use app\common\service\PredisService;
use app\common\service\RedisDelay;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
use think\Exception;

class RedisDelayQueue extends Command
{

    protected function configure()
    {
        $this->setName('RedisDelayQueue')->setDescription('RedisDelayQueue running');
    }

    protected function execute(Input $input, Output $output)
    {
        $redis_delay_class = new RedisDelay();

        while (true) {

            try {
                //判断redis是否下线
//                if (!$redis->isConnected()) {
//                    Log::trace('handle_jobs.log', [
//                        'msg' => 'redis下线'
//                    ]);
//                    throw new \Exception('redis下线');
//                }
                #echo microtime() . "\n";
                $redis_delay_class->run();
                usleep(100000);

            } catch (Exception $exception) {

                Log::trace('handle_delay_redis_queue_jobs.log', [
                    'msg' => '执行延时队列任务失败!',
                    'params' => [
                        'msg' => $exception->getMessage(),
                        'file' => $exception->getFile(),
                        'code' => $exception->getCode(),
                        'line' => $exception->getLine(),
                        #'info' => $info ?? []
                    ]
                ]);

                throw new \Exception('执行公共任务失败!' . json_encode([
                        'params' => [
                            'msg' => $exception->getMessage(),
                            'file' => $exception->getFile(),
                            'code' => $exception->getCode(),
                            'line' => $exception->getLine(),
                            #'info' => $info ?? []
                        ]
                    ]));
            }

        }
    }

}

解决 laravel-admin between datetime 假如数据库是时间戳int类型无法筛选。

laravel-admin默认的between->datetime(),查询默认是datetime类型,但是假如数据库是时间戳类型就会报错,又不想改底层文件的话可以试试加自定义筛选功能...

阅读全文

php解析英文语句,自动分解。

参考:https://www.php.net/manual/en/function.str-split.php 最近碰到一个问题,客户的英文地址太长,超出接口api字段长度,所以需要解析下语句分解发送。 ...

阅读全文

记录一个laravel-excel导出表格值为0导出excel显示空的解决方法。

最近在使用laravel-excel导出表格的时候,发现假如字段值为0的情况下,导出的excel中直接显示为空,找到一个方法解决,如下. 在laravel-excel的config配置中...

阅读全文

欢迎留言