<?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);
}
}