RedisUtil.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Utils;
  4. use Hyperf\Context\ApplicationContext;
  5. use Hyperf\Coroutine\Coroutine;
  6. /**
  7. * 全局redis键值配置
  8. */
  9. class RedisUtil
  10. {
  11. /**
  12. * 设置redis key
  13. *
  14. * @var string
  15. */
  16. private string $Key;
  17. private $Redis;
  18. private static $instances = [];
  19. /**
  20. * @param string $key
  21. * @throws \Psr\Container\ContainerExceptionInterface
  22. * @throws \Psr\Container\NotFoundExceptionInterface
  23. */
  24. public function __construct(string $key)
  25. {
  26. $container = ApplicationContext::getContainer();
  27. $this->Redis = $container->get(\Redis::class);
  28. $this->Key = $key;
  29. }
  30. /**
  31. * @param string $key
  32. * @param string ...$dynamic key动态值拼接 示例 RedisUtil::getInstance('key','user_no'); 则代表着 用户编号唯一的key
  33. * @return RedisUtil|mixed
  34. * @throws \Exception
  35. */
  36. public static function getInstance(string $key, string ...$dynamic)
  37. {
  38. $dynamics = '';
  39. foreach ($dynamic as $k => $v) {
  40. if ($k == 0) {
  41. $dynamics .= $v;
  42. } else {
  43. $dynamics .= '_' . $v;
  44. }
  45. }
  46. $instanceName = Coroutine::id() . uniqid();//协程id&唯一id组成
  47. if (!isset(self::$instances[$instanceName])) {
  48. self::$instances[$instanceName] = new self($key . $dynamics);
  49. Coroutine::defer(function () use ($instanceName) {
  50. unset(self::$instances[$instanceName]);
  51. });
  52. }
  53. return self::$instances[$instanceName];
  54. }
  55. /**
  56. * 生成唯一标识
  57. * @param int $length 生成长度
  58. * @return string
  59. */
  60. public function createNo(int $length = 5): string
  61. {
  62. $redis = $this->Redis;
  63. $key = $this->Key;
  64. //指定键值新增+1 并获取
  65. $reqNo = $redis->incr($key);
  66. //值长度已达到5位,且首位已为9,初始化
  67. $reqNoString = (string)$reqNo;
  68. if (strlen($reqNoString) >= $length && substr($reqNoString, 0, 1) == 9) {
  69. $redis->set($key, 1);
  70. $reqNo = 1;
  71. }
  72. if ($reqNo == 1) {
  73. //设置月末到期
  74. $expire = strtotime(date('Y-m', strtotime("+1 month")) . '-01 00:00:00') - time();
  75. $redis->expire($key, $expire);
  76. }
  77. return sprintf("%0{$length}d", $reqNo);
  78. }
  79. /**
  80. * 限制接口请求次数
  81. * @param string $path 可以是路由,可以是地址,可以是ip,总之是 string 即可
  82. * @param int $second 秒
  83. * @param int $count 多长时间内限制请求次数
  84. * @return bool
  85. */
  86. public function requestLimit(string $path, int $second, int $count): bool
  87. {
  88. $redis = $this->Redis;
  89. $key = $this->Key . md5($path);
  90. /*$_cou = $redis->incr($key);
  91. if ($_cou > $count) return false;
  92. if ($_cou == 1) $redis->expire($key, $second);
  93. unset($redis, $_cou);
  94. return true;*/
  95. $luaScript = <<<lua
  96. local count = redis.call("incr",KEYS[1])
  97. if count == 1 then
  98. redis.call('expire',KEYS[1],ARGV[2])
  99. end
  100. if count > tonumber(ARGV[1]) then
  101. return 0
  102. end
  103. return 1
  104. lua;
  105. return (bool)$redis->eval($luaScript, [$key, $count, $second], 1);
  106. }
  107. /**
  108. * 限制尝试次数 (设定时间内)
  109. * @param int $second 秒
  110. * @param int $Times 次数
  111. * @return false|int
  112. */
  113. public function tryTimes(int $second, int $Times = 5)
  114. {
  115. $redis = $this->Redis;
  116. $key = $this->Key;
  117. //指定键值新增+1 并获取
  118. $count = $redis->incr($key);
  119. if ($count > $Times) {
  120. return false;
  121. }
  122. //设置过期时间
  123. if ($count == 1) {
  124. $redis->expire($key, $second);
  125. }
  126. return (int)$count;
  127. }
  128. /**
  129. * 加锁
  130. * @param int $timeout 锁的过期时间
  131. * @return false|string
  132. */
  133. public function Lock(int $timeout = 3)
  134. {
  135. $redis = $this->Redis;
  136. $lockName = $this->Key;//锁的名字
  137. #获取唯一标识符
  138. $identifier = uniqid();
  139. #锁的过期时间
  140. $end = time() + $timeout;
  141. #循环获取锁
  142. while (time() < $end) {
  143. #查看$lockName是否被上锁,为$lockName设置过期时间,防止死锁
  144. if ($redis->set($lockName, $identifier, ['ex' => $timeout, 'nx'])) {
  145. return $identifier;
  146. }
  147. #停止0.001ms
  148. usleep(1);
  149. }
  150. return false;
  151. }
  152. /**
  153. * 释放锁
  154. * @param string $identifier 锁的唯一值
  155. * @return bool
  156. */
  157. public function releaseLock(string $identifier): bool
  158. {
  159. $redis = $this->Redis;
  160. $lockName = $this->Key;//锁的名字
  161. // 判断是锁有没有被其他客户端修改
  162. if ($redis->get($lockName) != $identifier) {
  163. #其他客户端修改了锁,不能删除别人的锁
  164. return false;
  165. }
  166. #释放锁
  167. $redis->del($lockName);
  168. return true;
  169. }
  170. public function setex(mixed $value, int $second)
  171. {
  172. $redis = $this->Redis;
  173. $key = $this->Key;
  174. return $redis->setex($key, $second, $value);
  175. }
  176. public function get()
  177. {
  178. $redis = $this->Redis;
  179. $key = $this->Key;
  180. return $redis->get($key);
  181. }
  182. public function del()
  183. {
  184. $redis = $this->Redis;
  185. $key = $this->Key;
  186. return $redis->del($key);
  187. }
  188. //加入list
  189. public function lPush(array $value)
  190. {
  191. $redis = $this->Redis;
  192. $key = $this->Key;
  193. return $redis->lPush($key, $value);
  194. }
  195. //删除右边
  196. public function rPop()
  197. {
  198. $redis = $this->Redis;
  199. $key = $this->Key;
  200. return $redis->rPop($key);
  201. }
  202. //list长度
  203. public function lLen()
  204. {
  205. $redis = $this->Redis;
  206. $key = $this->Key;
  207. return $redis->llen($key);
  208. }
  209. //删,移除有序集key中的一个或多个成员,不存在的成员将被忽略。[del_num | 0]
  210. public function zRem($member)
  211. {
  212. $redis = $this->Redis;
  213. $key = $this->Key;
  214. return $redis->zRem($key,$member);
  215. }
  216. // 增加分数
  217. public function zIncrBy($value,$member)
  218. {
  219. $redis = $this->Redis;
  220. $key = $this->Key;
  221. return $redis->zIncrBy($key, $value, $member);
  222. }
  223. //查,通过(score从大到小)【排序名次范围】拿member值,返回有序集key中,【指定区间内】的成员 [array | null]
  224. public function zRevRange(int $start, int $end, mixed $scores = null)
  225. {
  226. $redis = $this->Redis;
  227. $key = $this->Key;
  228. return $redis->zRevRange($key, $start, $end, $scores);
  229. }
  230. public function zRevRank(mixed $member)
  231. {
  232. $redis = $this->Redis;
  233. $key = $this->Key;
  234. return $redis->zRevRank($key, $member);
  235. }
  236. //查,取值【value|false】
  237. public function hGet($member)
  238. {
  239. $redis = $this->Redis;
  240. $key = $this->Key;
  241. return $redis->hGet($key,$member);
  242. }
  243. //删,删除指定下标的field,不存在的域将被忽略,[num | false]
  244. public function hDel($field)
  245. {
  246. $redis = $this->Redis;
  247. $key = $this->Key;
  248. return $redis->hDel($key,$field);
  249. }
  250. //增,改,将哈希表key中的域field的值设为value,不存在创建,存在就覆盖【1 | 0】
  251. public function hSet($member,$value)
  252. {
  253. $redis = $this->Redis;
  254. $key = $this->Key;
  255. return $redis->hSet($key,$member,$value);
  256. }
  257. //增,改,将一个或多个member元素及其score值加入到有序集key当中。[num | 0]
  258. public function zAdd($score, $member)
  259. {
  260. $redis = $this->Redis;
  261. $key = $this->Key;
  262. return $redis->zAdd($key,$score,$member);
  263. }
  264. }