php redis类 含分布式锁

<?php

namespace App\Common;


use App\Exception\BizException;
use Hyperf\Context\ApplicationContext;
use Hyperf\Context\Context;
use Hyperf\Database\Exception\QueryException;
use Hyperf\Redis\RedisFactory;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use function \Hyperf\Config\config;

class RedisHelper
{
    // 连接池名称
    private static $redisClients = [];
    private static $connection = 'default';
    private static $configs = [];
    private const RD_CTX_CONN_KEY = 'RedisHelperConnName';

    /**
     * Redis客户端连接
     * @param string|string $conn
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    private static function redisClient(string $conn = 'default')
    {
        empty(self::$redisClients[$conn]) && self::$redisClients[$conn] = ApplicationContext::getContainer()->get(RedisFactory::class)->get($conn);
        return self::$redisClients[$conn];
    }

    /**
     * 获取RedisHelper克隆对象
     * @param string $conn
     * @param bool $reInit
     * @return RedisHelper
     */
    public static function connection($conn = 'default', $reInit = false)
    {
        Context::set(self::RD_CTX_CONN_KEY, $conn);
        if ($reInit === true) {
            unset(self::$redisClients[$conn]);
        }
        return new self();
    }


    /**
     * 设置redis值
     * @param $key
     * @param $value
     * @param null $timeout
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function set($key, $value, $timeout = null)
    {
        return self::redisClient(self::_redisConnectionName())->set($this->formatKey($key), $this->data2Value($value), $timeout);
    }

    /**
     * 获取redis值
     * @param $key
     * @return mixed|null
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function get($key)
    {
        $value = self::redisClient(self::_redisConnectionName())->get($this->formatKey($key));
        return $value !== false ? $this->value2Data($value) : NULL;
    }

    /**
     * 删除key
     * @param $key
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function delete($key)
    {
        return self::redisClient(self::_redisConnectionName())->del($this->formatKey($key));
    }

    public function sIsMember($key, $value)
    {
        return self::redisClient(self::_redisConnectionName())->sIsMember($this->formatKey($key), $value);
    }

    public function sAdd($key, ...$value)
    {
        return self::redisClient(self::_redisConnectionName())->sAdd($this->formatKey($key), ...$value);
    }

    public function sRem($key, ...$value)
    {
        return self::redisClient(self::_redisConnectionName())->sRem($this->formatKey($key), ...$value);
    }

    public function hSet($key, $hashKey, $value)
    {
        return self::redisClient(self::_redisConnectionName())->hSet($this->formatKey($key), $hashKey, $value);
    }

    public function hDel($key, $hashKey)
    {
        return self::redisClient(self::_redisConnectionName())->hDel($this->formatKey($key), $hashKey);
    }

    public function hMSet($key, $hashKeys)
    {
        return self::redisClient(self::_redisConnectionName())->hMSet($this->formatKey($key), $hashKeys);
    }

    public function hGet($key, $hashKey)
    {
        return self::redisClient(self::_redisConnectionName())->hGetAll($this->formatKey($key), $hashKey);
    }

    public function hGetAll($key)
    {
        return self::redisClient(self::_redisConnectionName())->hGetAll($this->formatKey($key));
    }

    public function multi()
    {
        return self::redisClient(self::_redisConnectionName())->multi();
    }

    public function exec()
    {
        return self::redisClient(self::_redisConnectionName())->exec();
    }

    /**
     *设置值以及超时时间
     * @param $key
     * @param null $timeout
     * @param null $value
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function setex($key, $timeout = null, $value = null)
    {
        return self::redisClient(self::_redisConnectionName())->setex($this->formatKey($key), $timeout, $this->data2Value($value));
    }

    /**
     * 缓存封装
     * @param $key
     * @param $fn
     * @param bool $cacheEmpty
     * @param int $expire
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function cacheData($key, $fn, $cacheEmpty = true, $expire = 600)
    {
        if (!is_callable($fn, true)) {
            throw new \Exception('抱歉,第二个入参[$fn]必须为函数!!', -1);
        }
        $_value = $this->get($key);
        if ($_value !== NULL) {
            return $_value;
        }
        $result = call_user_func($fn);
        if ($cacheEmpty || !empty($result)) {
            $this->setex($key, $expire, $result);
        }
        return $result;
    }

//    /**
//     * 分布式锁封装
//     * @param $key
//     * @param $fn
//     * @param $expires
//     * @param $tryTimes
//     * @param $code
//     * @param $msg
//     * @return false|mixed
//     * @throws BizException
//     * @throws ContainerExceptionInterface
//     * @throws NotFoundExceptionInterface
//     */
//    public function distributeLock($key, $fn, $expires = 5, $tryTimes = 400, $code = 999, $msg = '请求太频繁,稍后再试!')
//    {
//        if (!is_callable($fn, true)) {
//            throw new BizException('抱歉,第二个入参[$fn]必须为函数!!', -1);
//        }
//        while (true) {
//            self::$connection = 'lock';
//            if ($this->set($key, 1, ['nx', 'ex' => $expires])) {
//                break;
//            }
//            $tryTimes --;
//            if ($tryTimes <= 0) {
//                throw new BizException($msg, $code);
//            }
//            usleep(5);
//        }
//        try {
////            var_dump(
////                12312
////            );
//            $result = call_user_func($fn);
////            throw new BizException('抱歉,第二个入参[$fn]必须为函数!!', -1);
//        } catch (\Exception $e) {
//            throw $e;
//        } finally {
//            self::$connection = 'lock';
//            $res = $this->delete($key);
////            Logger::logger()->info(__METHOD__ . '| 删除锁', ['time' => date('Y-m-d H:i:s'), 'res' => $res]);
//        }
//        return $result;
//    }
    /**
     * 分布式锁封装
     * @param $key
     * @param $fn
     * @param int $expires
     * @param int $tryTimes
     * @param int $code
     * @param string $msg
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function distributeLock($key, $fn, $expires = 5, $tryTimes = 400, $code = 999, $msg = '请求太频繁,稍后再试!')
    {
        try {
            if (!is_callable($fn, true)) {
                throw new \Exception('抱歉,第二个入参[$fn]必须为函数!!', -1);
            }
            $try = 0;
//            self::$connection = 'lock';
            Context::set(self::RD_CTX_CONN_KEY, 'lock');
            while (true) {
                if ($this->set($key, 1, ['nx', 'ex' => $expires])) {
                    break;
                }
                $try++;
                if ($try > $tryTimes) {
                    throw new \Exception($msg, $code);
                }
                usleep(5);//休眠5毫秒
            }
            $result = call_user_func($fn,__FUNCTION__);
//            self::$connection = 'lock';
            Context::set(self::RD_CTX_CONN_KEY, 'lock');
            $this->delete($key);
        } catch (QueryException $e) {
//            self::$connection = 'lock';
            Context::set(self::RD_CTX_CONN_KEY, 'lock');
            $this->delete($key);
            throw new \Exception($e->getMessage(), intval($e->getCode()));
        } catch (BizException $e) {
//            self::$connection = 'lock';
            Context::set(self::RD_CTX_CONN_KEY, 'lock');
            $this->delete($key);
            throw new BizException($e->getMessage(), $e->getCode());
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage(), intval($e->getCode()));
        }
        return $result;
    }


    public function __call($name, $arguments)
    {
        return self::redisClient(self::_redisConnectionName())->$name(...$arguments);
    }

    public function data2Value($data = null)
    {
        return @serialize($data);
    }

    public function value2Data($value = null)
    {
        return @unserialize($value);
    }

    public function formatKey($key)
    {
        $config = $this->config(self::_redisConnectionName());
        !empty($config['pre']) && $key = sprintf('%s:%s', $config['pre'], $key);
        return $key;
    }

    private function config($conn)
    {
        empty(self::$configs[$conn]) && self::$configs[$conn] = config(sprintf('redis.%s', self::_redisConnectionName()));
        return self::$configs[$conn];
    }

    /**
     * @param string $key
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function exists(string $key): mixed
    {
        return self::redisClient(self::_redisConnectionName())->exists($this->formatKey($key));
    }

    /**
     * @param string $key
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function incr(string $key): mixed
    {
        return self::redisClient(self::_redisConnectionName())->incr($this->formatKey($key));
    }

    /**
     * @param string $key
     * @param int $step
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function incrBy(string $key, int $step = 1): mixed
    {
        return self::redisClient(self::_redisConnectionName())->incrBy($this->formatKey($key), $step);
    }

    /**
     * @param string $key
     * @param int $expire
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function expire(string $key, int $expire = 0): mixed
    {
        return self::redisClient(self::_redisConnectionName())->expire($this->formatKey($key), $expire);
    }

    /**
     * @param string $key
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function Ttl(string $key): int
    {
        return self::redisClient(self::_redisConnectionName())->ttl($this->formatKey($key));
    }

    private  static function _redisConnectionName(){
        return Context::get(self::RD_CTX_CONN_KEY);
    }
}

 

posted @ 2025-02-27 11:31  jk波  阅读(8)  评论(0)    收藏  举报