|
@@ -0,0 +1,326 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace app\utils;
|
|
|
+
|
|
|
+class RedisUtil
|
|
|
+{
|
|
|
+ private $Key;
|
|
|
+ protected $Redis;
|
|
|
+ private static $instances = [];
|
|
|
+ protected $options = [
|
|
|
+ 'host' => '127.0.0.1',
|
|
|
+ 'port' => 6379,
|
|
|
+ 'password' => '',
|
|
|
+ 'select' => 0,
|
|
|
+ 'timeout' => 0,//
|
|
|
+ 'expire' => 0,
|
|
|
+ 'persistent' => false,
|
|
|
+ 'prefix' => '',
|
|
|
+ ];
|
|
|
+
|
|
|
+ public function __construct(string $key)
|
|
|
+ {
|
|
|
+ // 判断是否有扩展(若是你的apache没reids扩展就会抛出这个异常)
|
|
|
+ if (!extension_loaded('redis')) {
|
|
|
+ throw new \BadFunctionCallException('not support: redis');
|
|
|
+ }
|
|
|
+ $this->options = config('redis');
|
|
|
+ $func = $this->options['persistent'] ? 'pconnect' : 'connect';// 判断是否长链接
|
|
|
+ $this->Redis = new \Redis;
|
|
|
+ $redis = $this->Redis->$func($this->options['host'], $this->options['port'], $this->options['timeout']);
|
|
|
+ if (!$redis) {
|
|
|
+ throw new \BadFunctionCallException('connection fail: redis');
|
|
|
+ }
|
|
|
+ if ($this->options['password'] != '') {
|
|
|
+ $this->Redis->auth($this->options['password']);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (0 != $this->options['select']) {
|
|
|
+ $this->Redis->select($this->options['select']);
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->Key = $this->options['prefix'] . ':' . $key;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param string $key
|
|
|
+ * @param string ...$dynamic key动态值拼接
|
|
|
+ * @return RedisUtil|mixed
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public static function getInstance(string $key, string ...$dynamic)
|
|
|
+ {
|
|
|
+ $dynamics = '';
|
|
|
+ foreach ($dynamic as $k => $v) {
|
|
|
+ if ($k == 0) {
|
|
|
+ $dynamics .= $v;
|
|
|
+ } else {
|
|
|
+ $dynamics .= '_' . $v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $instanceName = uniqid();
|
|
|
+ if (!isset(self::$instances[$instanceName])) {
|
|
|
+ self::$instances[$instanceName] = new self($key . $dynamics);
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::$instances[$instanceName];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成唯一标识
|
|
|
+ * @param int $length 生成长度
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ public function createNo(int $length = 5): string
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ //指定键值新增+1 并获取
|
|
|
+ $reqNo = $redis->incr($key);
|
|
|
+
|
|
|
+ //值长度已达到5位,且首位已为9,初始化
|
|
|
+ if (strlen($reqNo) >= $length && substr($reqNo, 0, 1) == 9) {
|
|
|
+ $redis->set($key, 1);
|
|
|
+ $reqNo = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($reqNo == 1) {
|
|
|
+ //设置月末到期
|
|
|
+ $expire = strtotime(date('Y-m', strtotime("+1 month")) . '-01 00:00:00') - time();
|
|
|
+ $redis->expire($key, $expire);
|
|
|
+ }
|
|
|
+
|
|
|
+ return sprintf("%0{$length}d", $reqNo);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 限制尝试次数 (设定时间内)
|
|
|
+ * @param int $second 秒
|
|
|
+ * @param int $Times 次数
|
|
|
+ * @return false|int
|
|
|
+ */
|
|
|
+ public function tryTimes(int $second, int $Times = 5)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ //指定键值新增+1 并获取
|
|
|
+ $count = $redis->incr($key);
|
|
|
+ if ($count > $Times) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //设置过期时间
|
|
|
+ if ($count == 1) {
|
|
|
+ $redis->expire($key, $second);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (int)$count;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加锁
|
|
|
+ * @param int $timeout 锁的过期时间
|
|
|
+ * @return false|string
|
|
|
+ */
|
|
|
+ public function Lock(int $timeout = 10)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $lockName = $this->Key;//锁的名字
|
|
|
+
|
|
|
+ #获取唯一标识符
|
|
|
+ $identifier = uniqid();
|
|
|
+
|
|
|
+ #锁的过期时间
|
|
|
+ $end = time() + $timeout;
|
|
|
+
|
|
|
+ #循环获取锁
|
|
|
+ while (time() < $end) {
|
|
|
+ #查看$lockName是否被上锁,为$lockName设置过期时间,防止死锁
|
|
|
+ if ($redis->set($lockName, $identifier, ['ex' => $timeout, 'nx'])) {
|
|
|
+ return $identifier;
|
|
|
+ }
|
|
|
+
|
|
|
+ #停止0.001ms
|
|
|
+ usleep(0.001);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 释放锁
|
|
|
+ * @param string $identifier 锁的唯一值
|
|
|
+ * @return bool
|
|
|
+ */
|
|
|
+ public function releaseLock(string $identifier): bool
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $lockName = $this->Key;//锁的名字
|
|
|
+
|
|
|
+ // 判断是锁有没有被其他客户端修改
|
|
|
+ if ($redis->get($lockName) != $identifier) {
|
|
|
+ #其他客户端修改了锁,不能删除别人的锁
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ #释放锁
|
|
|
+ $redis->del($lockName);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function setex($value, int $second)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->setex($key, $second, $value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function get()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->get($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function del()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->del($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ //加入list
|
|
|
+ public function lPush($value)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->lPush($key, $value);
|
|
|
+ }
|
|
|
+
|
|
|
+ //删除右边
|
|
|
+ public function rPop()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->rPop($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function rPush($value)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->rPush($key, $value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function lPopLine($num)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ $pipe = $redis->multi(\Redis::PIPELINE);
|
|
|
+ $pipe->lRange($key, 0, $num - 1);
|
|
|
+ $pipe->lTrim($key, $num, -1);
|
|
|
+ return $pipe->exec();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //list长度
|
|
|
+ public function lLen()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->llen($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function zadd($score, $member)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->zadd($key, $score, $member);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function zcard()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->zcard($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function zrem($member)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->zrem($key, $member);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递增返回
|
|
|
+ public function zRange($start_score = 0, $end_score = -1)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->zrange($key, $start_score, $end_score);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递减返回
|
|
|
+ public function zRevRange($start_score = 0, $end_score = -1)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+
|
|
|
+ $key = $this->Key;
|
|
|
+
|
|
|
+ return $redis->zRevRange($key, $start_score, $end_score);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hSet($field, $value)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+ return $redis->hset($key, $field, $value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hKeys()
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+ return $redis->hkeys($key);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hGet($field)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+ return $redis->hget($key, $field);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hDel($field)
|
|
|
+ {
|
|
|
+ $redis = $this->Redis;
|
|
|
+ $key = $this->Key;
|
|
|
+ return $redis->hdel($key, $field);
|
|
|
+ }
|
|
|
+}
|