diff --git a/opsli-plugins/opsli-plugins-redis/pom.xml b/opsli-plugins/opsli-plugins-redis/pom.xml new file mode 100644 index 0000000..13123eb --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/pom.xml @@ -0,0 +1,27 @@ + + + + opsli-plugins + org.opsliframework.boot + 1.0.0 + ../pom.xml + + + 4.0.0 + opsli-plugins-redis + ${project.parent.version} + + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + + \ No newline at end of file diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/RedisPlugin.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/RedisPlugin.java new file mode 100644 index 0000000..62c40f8 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/RedisPlugin.java @@ -0,0 +1,1518 @@ +package org.opsli.plugins.redis; + +import lombok.extern.slf4j.Slf4j; +import org.opsli.plugins.redis.exception.RedisPluginException; +import org.opsli.plugins.redis.msg.RedisMsg; +import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage; +import org.opsli.plugins.redis.scripts.RedisPluginScript; +import org.opsli.plugins.redis.scripts.RedisScriptCache; +import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisStringCommands; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ZSetOperations; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Redis 插件类 + * @author parker + * @date 2020-09-14 + */ +@Slf4j +@Component +public class RedisPlugin { + + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private RedisScriptCache redisScriptCache; + + // ===================== 基础相关 ===================== + + /** + * 判断key是否存在 + * + * @param key Redis键 + * @return boolean + */ + public boolean hasKey(String key) { + Boolean ret = null; + try { + ret = redisTemplate.hasKey(key); + } catch (Exception e) { + log.error(e.getMessage(),e); + return false; + } + return ret != null && ret; + } + + /** + * 删除1个key + * + * @param key Redis键 + * @return 成功删除的个数 + */ + public boolean del(String key) { + Boolean ret = redisTemplate.delete(key); + return ret != null && ret; + } + /** + * 删除多个key + * + * @param keys Redis键集合 + * @return 成功删除的个数 + */ + public Long del(Collection keys) { + Long ret = null; + if (keys != null && keys.size() > 0) { + ret = redisTemplate.delete(keys); + } + return ret; + } + + /** + * 使用脚本 + * @param scriptsEnum 脚本枚举 + * @param keys 多值 + * @param argv 多参数 + * @return object + */ + public Object callScript(RedisScriptsEnum scriptsEnum, List keys, List argv) { + // 获得Script脚本 + RedisPluginScript script = redisScriptCache.getScript(scriptsEnum); + if(script == null){ + return false; + } + RedisScript of = RedisScript.of(script.getScript()); + return redisTemplate.execute(of,keys,argv); + } + + // ===================== Redis 锁相关 ===================== + /* + * 分布式锁需要考虑的问题 + * 1、这把锁没有失效时间,一旦解锁操作失败,就会导致锁记录一直在tair中,其他线程无法再获得到锁。 + * 2、这把锁只能是非阻塞的,无论成功还是失败都直接返回。 + * 3、这把锁是非重入的,一个线程获得锁之后,在释放锁之前,无法再次获得该锁,因为使用到的key在tair中已经存在。无法再执行put操作。 + * */ + + /** + * Redis 加分布式锁 + * @param lockName 锁名称 + * @param acquireTimeOut 尝试获取锁等待时间 + * @param lockTimeOut 锁有效时间 + * @return identifier 很重要,解锁全靠他 唯一凭证 + */ + public String lockWithTimeOut(String lockName, long acquireTimeOut, int lockTimeOut) { + String identifier = UUID.randomUUID().toString(); + List keys = Collections.singletonList("lock:" + lockName); + List argv = Arrays.asList(identifier, + String.valueOf(lockTimeOut)); + + long acquireTimeEnd = System.currentTimeMillis() + acquireTimeOut; + boolean acquired = false; + // 尝试获得锁 + while (!acquired && (System.currentTimeMillis() < acquireTimeEnd)) { + Long ret = (Long) this.callScript(RedisScriptsEnum.REDIS_LOCK, keys, argv); + if(ret == null){ + break; + } + if (1 == ret){ + acquired = true; + } else { + try { + Thread.sleep(10); + } catch (InterruptedException ignored) { + log.error(ignored.getMessage(),ignored); + } + } + } + return acquired ? identifier : null; + } + + /** + * Redis 释放分布式锁 + * @param lockName 锁名称 + * @param identifier 唯一凭证 + * @return boolean + */ + public boolean unLock(String lockName, String identifier) { + List keys = Collections.singletonList("lock:" + lockName); + List argv = Collections.singletonList(identifier); + Long ret = (Long) this.callScript(RedisScriptsEnum.REDIS_UN_LOCK, keys, argv); + if(ret == null){ + return false; + } + return 1 == ret; + } + + + // ===================== 缓存有效时间相关 ===================== + + /** + * 指定缓存有效时间 + * + * @param key Redis键 + * @param timeout 超时时间(秒) + * @return boolean + */ + public boolean expire(String key, long timeout) { + return this.expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置缓存有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return boolean + */ + public boolean expire(String key, long timeout, TimeUnit unit) { + Boolean ret = null; + try { + if(timeout > 0){ + ret = redisTemplate.expire(key, timeout, unit); + } + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret != null && ret; + } + + /** + * 获得缓存有效时间 + * + * @param key Redis键 + * @return 超时时间(秒) 0 则表示永久有效 + */ + public Long getExpire(String key) { + return this.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 获得缓存有效时间 + * + * @param key Redis键 + * @param unit 时间单位 + * @return 超时时间 0 则表示永久有效 + */ + public Long getExpire(String key, TimeUnit unit) { + if(key == null || "".equals(key)){ + throw new RedisPluginException(RedisMsg.EXCEPTION_KEY_NULL); + } + return redisTemplate.getExpire(key, unit); + } + + + + // ===================== 普通对象 ===================== + + /** + * 获取普通对象 + * + * @param key Redis键 + * @return 对象 + */ + public Object get(String key) { + if(key == null){ + return null; + } + return redisTemplate.opsForValue().get(key); + } + + + + /** + * 存入普通对象 + * 无时间限制 + * + * @param key Redis键 + * @param value 值 + * @return boolean + */ + public boolean put(String key, Object value) { + boolean ret = false; + try { + redisTemplate.opsForValue().set(key, value); + ret = true; + } catch (Exception e) { + log.error(e.getMessage(),e); + } + return ret; + } + + /** + * 存入普通对象 + * 有时间限制 + * + * @param key Redis键 + * @param value 值 + * @param timeout 有效期,单位秒 + * @return boolean + */ + public boolean put(String key, Object value, long timeout) { + return this.put(key, value, timeout, TimeUnit.SECONDS); + } + + /** + * 存入普通对象 + * 有时间限制 + * + * @param key Redis键 + * @param value 值 + * @param timeout 有效期,单位秒 + * @param unit 时间单位 + * @return boolean + */ + public boolean put(String key, Object value, long timeout, TimeUnit unit) { + boolean ret = false; + try { + if (timeout > 0) { + redisTemplate.opsForValue().set(key, value, timeout, unit); + } else { + put(key, value); + } + ret = true; + } catch (Exception e) { + log.error(e.getMessage(),e); + } + return ret; + } + + + /** + * 递增 原子性++ increment + * + * @param key Redis键 + * @return + */ + public Long increment(String key) { + return this.increment(key,1); + } + + /** + * 递增 原子性++ increment + * + * @param key Redis键 + * @param by 增加 i + * @return + */ + public Long increment(String key, long by) { + if(key == null || "".equals(key)){ + throw new RedisPluginException(RedisMsg.EXCEPTION_KEY_NULL); + } + if (by < 0) { + throw new RedisPluginException(RedisMsg.EXCEPTION_INCREMENT); + } + return redisTemplate.opsForValue().increment(key, by); + } + + /** + * 递减 原子性 -- decrement + * + * @param key Redis键 + * @return + */ + public Long decrement(String key) { + return this.decrement(key,1); + } + + /** + * 递减 原子性 -- decrement + * + * @param key Redis键 + * @param by 减少 i + * @return + */ + public Long decrement(String key, long by) { + if(key == null || "".equals(key)){ + throw new RedisPluginException(RedisMsg.EXCEPTION_KEY_NULL); + } + if (by < 0) { + throw new RedisPluginException(RedisMsg.EXCEPTION_DECREMENT); + } + return redisTemplate.opsForValue().decrement(key, by); + } + + // ===================== 二进制位图 ===================== + /* + * 比如 用在用户 登陆统计等 + * */ + + /** + * 设置二进制位图 + * @param key Redis键 + * @param offset 偏移量 + * @param value 0:false 1:true + * @return boolean + */ + public boolean bPut(String key, long offset, boolean value) { + Boolean ret = redisTemplate.opsForValue().setBit(key, offset, value); + return ret != null && ret; + } + + /** + * 获得二进制位图 对应偏移量的值 + * @param key Redis键 + * @param offset 偏移量 + * @return boolean + */ + public boolean bGet(String key, long offset) { + Boolean ret = redisTemplate.opsForValue().getBit(key, offset); + return ret != null && ret; + } + + + /** + * 统计对应的bitmap上value为1的数量 + * + * @param key bitmap的key + * @return value等于1的数量 + */ + public Long bGetCount(String key) { + return redisTemplate.execute((RedisCallback) con -> con.bitCount(key.getBytes())); + } + + /** + * 统计指定范围中value为1的数量 + * + * @param key bitMap中的key + * @param start 该参数的单位是byte(1byte=8bit),{@code setBit(key,7,true);}进行存储时,单位是bit。那么只需要统计[0,1]便可以统计到上述set的值。 + * @param end 该参数的单位是byte。 + * @return 在指定范围[start*8,end*8]内所有value=1的数量 + */ + public Long bGetCount(String key, int start, int end) { + return redisTemplate.execute((RedisCallback) con -> con.bitCount(key.getBytes(), start, end)); + } + + + /** + * 对一个或多个保存二进制的字符串key进行元操作,并将结果保存到saveKey上。 + *

+ * bitop and saveKey key [key...],对一个或多个key逻辑并,结果保存到saveKey。 + * bitop or saveKey key [key...],对一个或多个key逻辑或,结果保存到saveKey。 + * bitop xor saveKey key [key...],对一个或多个key逻辑异或,结果保存到saveKey。 + * bitop xor saveKey key,对一个或多个key逻辑非,结果保存到saveKey。 + *

+ * + * @param op 元操作类型; + * @param saveKey 元操作后将结果保存到saveKey所在的结构中。 + * @param desKey 需要进行元操作的类型。 + * @return 1:返回元操作值。 + */ + public Long bGetOp(RedisStringCommands.BitOperation op, String saveKey, String... desKey) { + byte[][] bytes = new byte[desKey.length][]; + for (int i = 0; i < desKey.length; i++) { + bytes[i] = desKey[i].getBytes(); + } + return redisTemplate.execute((RedisCallback) con -> con.bitOp(op, saveKey.getBytes(), bytes)); + } + + /** + * 对一个或多个保存二进制的字符串key进行元操作,并将结果保存到saveKey上,并返回统计之后的结果。 + * + * @param op 元操作类型; + * @param saveKey 元操作后将结果保存到saveKey所在的结构中。 + * @param desKey 需要进行元操作的类型。 + * @return 返回saveKey结构上value=1的所有数量值。 + */ + public Long bGetOpResult(RedisStringCommands.BitOperation op, String saveKey, String... desKey) { + this.bGetOp(op, saveKey, desKey); + return this.bGetCount(saveKey); + } + + // ===================== Hash/Map ===================== + + /** + * 获取存储在哈希表中指定字段的值 + * + * @param key + * @param field + * @return + */ + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + /** + * 获取所有给定字段的值 + * + * @param key + * @return + */ + public Map hGetAll(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 获取所有给定字段的值 + * + * @param key + * @param fields + * @return + */ + public List hMultiGet(String key, Collection fields) { + return redisTemplate.opsForHash().multiGet(key, fields); + } + + /** + * 添加一个Hash 数据 + * @param key + * @param field + * @param value + * @return + */ + public boolean hPut(String key, String field, Object value) { + boolean ret = false; + try { + redisTemplate.opsForHash().put(key, field, value); + ret = true; + } catch (Exception e) { + log.error(e.getMessage(),e); + } + return ret; + } + + + /** + * 添加一组Hash数据 + * @param key + * @param maps + * @return + */ + public boolean hPutAll(String key, Map maps) { + boolean ret = false; + try { + redisTemplate.opsForHash().putAll(key, maps); + ret = true; + } catch (Exception e) { + log.error(e.getMessage(),e); + } + return ret; + } + + + /** + * 仅当hashKey不存在时才设置 + * + * @param key + * @param field + * @param value + * @return + */ + public boolean hPutIfAbsent(String key, String field, Object value) { + Boolean ret = null; + try { + ret = redisTemplate.opsForHash().putIfAbsent(key, field, value); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret != null && ret; + } + + /** + * 删除一个或多个哈希表字段 + * + * @param key + * @param fields + * @return + */ + public Long hDelete(String key, Object... fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + + /** + * 删除一个或多个哈希表字段 + * + * @param key + * @param fields + * @return + */ + public Long hDelete(String key, Collection fields) { + return redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 查看哈希表 key 中,指定的字段是否存在 + * + * @param key + * @param field + * @return + */ + public boolean hHashKey(String key, String field) { + Boolean ret = null; + try { + ret = redisTemplate.opsForHash().hasKey(key, field); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + return ret != null && ret; + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param increment + * @return + */ + public Long hIncrBy(String key, Object field, long increment) { + return redisTemplate.opsForHash().increment(key, field, increment); + } + + /** + * 为哈希表 key 中的指定字段的整数值加上增量 increment + * + * @param key + * @param field + * @param delta + * @return + */ + public Double hIncrByFloat(String key, Object field, double delta) { + return redisTemplate.opsForHash().increment(key, field, delta); + } + + /** + * 获取所有哈希表中的字段 + * + * @param key + * @return + */ + public Set hKeys(String key) { + return redisTemplate.opsForHash().keys(key); + } + + /** + * 获取哈希表中字段的数量 + * + * @param key + * @return + */ + public Long hSize(String key) { + return redisTemplate.opsForHash().size(key); + } + + /** + * 获取哈希表中所有值 + * + * @param key + * @return + */ + public List hValues(String key) { + return redisTemplate.opsForHash().values(key); + } + + // ===================== List ===================== + + /** + * 通过索引获取列表中的元素 + * + * @param key + * @param index + * @return + */ + public Object lIndex(String key, long index) { + return redisTemplate.opsForList().index(key, index); + } + + /** + * 获取列表指定范围内的元素 + * + * @param key + * @param start + * 开始位置, 0是开始位置 + * @param end + * 结束位置, -1返回所有 + * @return + */ + public List lRange(String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPush(String key, String value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(String key, String... value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 存储在list头部 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushAll(String key, Collection value) { + return redisTemplate.opsForList().leftPushAll(key, value); + } + + /** + * 存储在list头部 + * 当list存在的时候才加入 + * + * @param key + * @param value + * @return + */ + public Long lLeftPushIfPresent(String key, String value) { + return redisTemplate.opsForList().leftPushIfPresent(key, value); + } + + /** + * 存储在list头部 + * 如果pivot存在,再pivot前面添加 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lLeftPush(String key, String pivot, String value) { + return redisTemplate.opsForList().leftPush(key, pivot, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPush(String key, String value) { + return redisTemplate.opsForList().rightPush(key, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPushAll(String key, String... value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 存储在list尾部 + * + * @param key + * @param value + * @return + */ + public Long lRightPushAll(String key, Collection value) { + return redisTemplate.opsForList().rightPushAll(key, value); + } + + /** + * 存储在list尾部 + * 为已存在的列表添加值 + * + * @param key + * @param value + * @return + */ + public Long lRightPushIfPresent(String key, String value) { + return redisTemplate.opsForList().rightPushIfPresent(key, value); + } + + /** + * 存储在list尾部 + * 在pivot元素的右边添加值 + * + * @param key + * @param pivot + * @param value + * @return + */ + public Long lRightPush(String key, String pivot, String value) { + return redisTemplate.opsForList().rightPush(key, pivot, value); + } + + /** + * 通过索引设置列表元素的值 + * + * @param key + * @param index + * 位置 + * @param value + */ + public boolean lSet(String key, long index, String value) { + boolean ret = false; + try { + redisTemplate.opsForList().set(key, index, value); + ret = true; + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret; + } + + /** + * 移出并获取列表的第一个元素 + * + * @param key + * @return 删除的元素 + */ + public Object lLeftPop(String key) { + return redisTemplate.opsForList().leftPop(key); + } + + /** + * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout + * 等待时间 + * @param unit + * 时间单位 + * @return + */ + public Object lBLeftPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().leftPop(key, timeout, unit); + } + + /** + * 移除并获取列表最后一个元素 + * + * @param key + * @return 删除的元素 + */ + public Object lRightPop(String key) { + return redisTemplate.opsForList().rightPop(key); + } + + /** + * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param key + * @param timeout + * 等待时间 + * @param unit + * 时间单位 + * @return + */ + public Object lBRightPop(String key, long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPop(key, timeout, unit); + } + + /** + * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 + * + * @param sourceKey + * @param destinationKey + * @return + */ + public Object lRightPopAndLeftPush(String sourceKey, String destinationKey) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey); + } + + /** + * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止 + * + * @param sourceKey + * @param destinationKey + * @param timeout + * @param unit + * @return + */ + public Object lBRightPopAndLeftPush(String sourceKey, String destinationKey, + long timeout, TimeUnit unit) { + return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey, + destinationKey, timeout, unit); + } + + /** + * 删除集合中值等于value得元素 + * + * @param key + * @param index + * index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素; + * index<0, 从尾部开始删除第一个值等于value的元素; + * @param value + * @return + */ + public Long lRemove(String key, long index, String value) { + return redisTemplate.opsForList().remove(key, index, value); + } + + /** + * 裁剪list + * + * @param key + * @param start + * @param end + */ + public boolean lTrim(String key, long start, long end) { + boolean ret = false; + try { + redisTemplate.opsForList().trim(key, start, end); + ret = true; + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret; + } + + /** + * 获取列表长度 + * + * @param key + * @return + */ + public Long lLen(String key) { + return redisTemplate.opsForList().size(key); + } + + // ===================== Set ===================== + + /** + * 判断集合是否包含value + * + * @param key + * @param value + * @return + */ + public boolean sHashKey(String key, Object value) { + Boolean ret = null; + try { + ret = redisTemplate.opsForSet().isMember(key, value); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret != null && ret; + } + + /** + * set添加元素 + * + * @param key + * @param values + * @return + */ + public Long sPut(String key, String... values) { + return redisTemplate.opsForSet().add(key, values); + } + + /** + * set移除元素 + * + * @param key + * @param values + * @return + */ + public Long sRemove(String key, Object... values) { + return redisTemplate.opsForSet().remove(key, values); + } + + /** + * set移除元素 + * + * @param key + * @param values + * @return + */ + public Long sRemove(String key, Collection values) { + return redisTemplate.opsForSet().remove(key, values); + } + + + /** + * 移除并返回集合的一个随机元素 + * + * @param key + * @return + */ + public Object sPop(String key) { + return redisTemplate.opsForSet().pop(key); + } + + /** + * 将元素value从一个集合移到另一个集合 + * + * @param key + * @param value + * @param destKey + * @return + */ + public boolean sMove(String key, String value, String destKey) { + Boolean ret = null; + try { + ret = redisTemplate.opsForSet().move(key, value, destKey); + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret != null && ret; + } + + /** + * 获取集合的大小 + * + * @param key + * @return + */ + public Long sSize(String key) { + return redisTemplate.opsForSet().size(key); + } + + /** + * 获取两个集合的交集 + * + * @param key + * @param otherKey + * @return + */ + public Set sIntersect(String key, String otherKey) { + return redisTemplate.opsForSet().intersect(key, otherKey); + } + + /** + * 获取key集合与多个集合的交集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sIntersect(String key, Collection otherKeys) { + return redisTemplate.opsForSet().intersect(key, otherKeys); + } + + /** + * key集合与otherKey集合的交集存储到destKey集合中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的交集存储到destKey集合中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sIntersectAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().intersectAndStore(key, otherKeys, + destKey); + } + + /** + * 获取两个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(String key, String otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * 获取key集合与多个集合的并集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sUnion(String key, Collection otherKeys) { + return redisTemplate.opsForSet().union(key, otherKeys); + } + + /** + * key集合与otherKey集合的并集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey); + } + + /** + * key集合与多个集合的并集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sUnionAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey); + } + + /** + * 获取两个集合的差集 + * + * @param key + * @param otherKey + * @return + */ + public Set sDifference(String key, String otherKey) { + return redisTemplate.opsForSet().difference(key, otherKey); + } + + /** + * 获取key集合与多个集合的差集 + * + * @param key + * @param otherKeys + * @return + */ + public Set sDifference(String key, Collection otherKeys) { + return redisTemplate.opsForSet().difference(key, otherKeys); + } + + /** + * key集合与otherKey集合的差集存储到destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long sDifference(String key, String otherKey, String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKey, + destKey); + } + + /** + * key集合与多个集合的差集存储到destKey中 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long sDifference(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForSet().differenceAndStore(key, otherKeys, + destKey); + } + + /** + * 获取集合所有元素 + * + * @param key + * @return + */ + public Set setMembers(String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 随机获取集合中的一个元素 + * + * @param key + * @return + */ + public Object sRandomMember(String key) { + return redisTemplate.opsForSet().randomMember(key); + } + + /** + * 随机获取集合中count个元素 + * + * @param key + * @param count + * @return + */ + public List sRandomMembers(String key, long count) { + return redisTemplate.opsForSet().randomMembers(key, count); + } + + /** + * 随机获取集合中count个元素并且去除重复的 + * + * @param key + * @param count + * @return + */ + public Set sDistinctRandomMembers(String key, long count) { + return redisTemplate.opsForSet().distinctRandomMembers(key, count); + } + + + // ===================== zSet ===================== + + /** + * 添加元素,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @param score + * @return + */ + public Boolean zAdd(String key, String value, double score) { + return redisTemplate.opsForZSet().add(key, value, score); + } + + /** + * + * @param key + * @param values + * @return + */ + public Long zAdd(String key, Set> values) { + return redisTemplate.opsForZSet().add(key, values); + } + + /** + * + * @param key + * @param values + * @return + */ + public Long zRemove(String key, Object... values) { + return redisTemplate.opsForZSet().remove(key, values); + } + + /** + * 增加元素的score值,并返回增加后的值 + * + * @param key + * @param value + * @param delta + * @return + */ + public Double zIncrementScore(String key, String value, double delta) { + return redisTemplate.opsForZSet().incrementScore(key, value, delta); + } + + /** + * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列 + * + * @param key + * @param value + * @return 0表示第一位 + */ + public Long zRank(String key, Object value) { + return redisTemplate.opsForZSet().rank(key, value); + } + + /** + * 返回元素在集合的排名,按元素的score值由大到小排列 + * + * @param key + * @param value + * @return + */ + public Long zReverseRank(String key, Object value) { + return redisTemplate.opsForZSet().reverseRank(key, value); + } + + /** + * 获取集合的元素, 从小到大排序 + * + * @param key + * @param start + * 开始位置 + * @param end + * 结束位置, -1查询所有 + * @return + */ + public Set zRange(String key, long start, long end) { + return redisTemplate.opsForZSet().range(key, start, end); + } + + /** + * 获取集合元素, 并且把score值也获取 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zRangeWithScores(String key, long start, + long end) { + return redisTemplate.opsForZSet().rangeWithScores(key, start, end); + } + + /** + * 根据Score值查询集合元素 + * + * @param key + * @param min + * 最小值 + * @param max + * 最大值 + * @return + */ + public Set zRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().rangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从小到大排序 + * + * @param key + * @param min + * 最小值 + * @param max + * 最大值 + * @return + */ + public Set> zRangeByScoreWithScores(String key, + double min, double max) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max); + } + + /** + * + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set> zRangeByScoreWithScores(String key, + double min, double max, long start, long end) { + return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max, + start, end); + } + + /** + * 获取集合的元素, 从大到小排序 + * + * @param key + * @param start + * @param end + * @return + */ + public Set zReverseRange(String key, long start, long end) { + return redisTemplate.opsForZSet().reverseRange(key, start, end); + } + + /** + * 获取集合的元素, 从大到小排序, 并返回score值 + * + * @param key + * @param start + * @param end + * @return + */ + public Set> zReverseRangeWithScores(String key, + long start, long end) { + return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, + end); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set zReverseRangeByScore(String key, double min, + double max) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max); + } + + /** + * 根据Score值查询集合元素, 从大到小排序 + * + * @param key + * @param min + * @param max + * @return + */ + public Set> zReverseRangeByScoreWithScores( + String key, double min, double max) { + return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, + min, max); + } + + /** + * + * @param key + * @param min + * @param max + * @param start + * @param end + * @return + */ + public Set zReverseRangeByScore(String key, double min, + double max, long start, long end) { + return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, + start, end); + } + + /** + * 根据score值获取集合元素数量 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zCount(String key, double min, double max) { + return redisTemplate.opsForZSet().count(key, min, max); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zSize(String key) { + return redisTemplate.opsForZSet().size(key); + } + + /** + * 获取集合大小 + * + * @param key + * @return + */ + public Long zZCard(String key) { + return redisTemplate.opsForZSet().zCard(key); + } + + /** + * 获取集合中value元素的score值 + * + * @param key + * @param value + * @return + */ + public Double zScore(String key, Object value) { + return redisTemplate.opsForZSet().score(key, value); + } + + /** + * 移除指定索引位置的成员 + * + * @param key + * @param start + * @param end + * @return + */ + public Long zRemoveRange(String key, long start, long end) { + return redisTemplate.opsForZSet().removeRange(key, start, end); + } + + /** + * 根据指定的score值的范围来移除成员 + * + * @param key + * @param min + * @param max + * @return + */ + public Long zRemoveRangeByScore(String key, double min, double max) { + return redisTemplate.opsForZSet().removeRangeByScore(key, min, max); + } + + /** + * 获取key和otherKey的并集并存储在destKey中 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, String otherKey, String destKey) { + return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey); + } + + /** + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zUnionAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForZSet() + .unionAndStore(key, otherKeys, destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKey + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, String otherKey, + String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, + destKey); + } + + /** + * 交集 + * + * @param key + * @param otherKeys + * @param destKey + * @return + */ + public Long zIntersectAndStore(String key, Collection otherKeys, + String destKey) { + return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys, + destKey); + } + + // ===================== 消息发布 ===================== + + /** + * Redis 消息发布 Push + * @param basePubMessage + */ + public boolean sendMessage(BaseSubMessage basePubMessage) { + if(basePubMessage == null){ + throw new RedisPluginException(RedisMsg.EXCEPTION_PUSH_SUB_NULL); + } + boolean ret = false; + try { + redisTemplate.convertAndSend(basePubMessage.getChannel(), basePubMessage.getJson()); + ret = true; + }catch (Exception e){ + log.error(e.getMessage(),e); + } + return ret; + } + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/conf/RedisPluginConfig.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/conf/RedisPluginConfig.java new file mode 100644 index 0000000..d966b1c --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/conf/RedisPluginConfig.java @@ -0,0 +1,102 @@ +package org.opsli.plugins.redis.conf; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.opsli.common.utils.PackageUtil; +import org.opsli.plugins.redis.msg.RedisMsg; +import org.opsli.plugins.redis.scripts.RedisPluginScript; +import org.opsli.plugins.redis.scripts.RedisScriptCache; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import javax.annotation.Resource; +import java.lang.reflect.Modifier; +import java.util.Set; + +/** + * @Author parker + * + * Redis 配置类 + * + */ +@Slf4j +@Configuration +public class RedisPluginConfig { + + @Resource + private LettuceConnectionFactory factory; + + /** + * RedisTemplate配置 + * 序列化设置 + */ + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(om); + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + // key采用String的序列化方式 + template.setKeySerializer(stringRedisSerializer); + // hash的key也采用String的序列化方式 + template.setHashKeySerializer(stringRedisSerializer); + // value序列化方式采用jackson + template.setValueSerializer(jackson2JsonRedisSerializer); + // hash的value序列化方式采用jackson + template.setHashValueSerializer(jackson2JsonRedisSerializer); + template.afterPropertiesSet(); + + // 开启事务 + template.setEnableTransactionSupport(true); + return template; + } + + + /** + * 加载脚本到缓存内 + * + * 默认开启 全局乐观锁 一劳永逸 + * + * @return + */ + @Bean + public RedisScriptCache loadScripts() { + + RedisScriptCache redisScriptCache = new RedisScriptCache(); + + // 拿到state包下 实现了 SystemEventState 接口的,所有子类 + Set> clazzSet = PackageUtil.listSubClazz(RedisPluginScript.class.getPackage().getName(), + true, + RedisPluginScript.class + ); + + for (Class aClass : clazzSet) { + // 位运算 去除抽象类 + if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){ + continue; + } + + // 通过反射 加载所有的 脚本 + try { + RedisPluginScript redisPluginScript = (RedisPluginScript) aClass.newInstance(); + redisScriptCache.putScript(redisPluginScript); + } catch (Exception e) { + log.error(RedisMsg.EXCEPTION_REFLEX.getMessage()); + } + + } + + return redisScriptCache; + } + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/enums/RedisPushSubMsgType.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/enums/RedisPushSubMsgType.java new file mode 100644 index 0000000..d877347 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/enums/RedisPushSubMsgType.java @@ -0,0 +1,11 @@ +package org.opsli.plugins.redis.enums; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.enums + * @Author: Parker + * @CreateTime: 2020-09-15 14:51 + * @Description: Redis消息订阅 消息类型 + */ +public enum RedisPushSubMsgType { +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/exception/RedisPluginException.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/exception/RedisPluginException.java new file mode 100644 index 0000000..454c446 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/exception/RedisPluginException.java @@ -0,0 +1,22 @@ +package org.opsli.plugins.redis.exception; + +import org.opsli.common.base.msg.BaseMsg; +import org.opsli.common.exception.ServiceException; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.mail.exception + * @Author: Parker + * @CreateTime: 2020-09-14 18:44 + * @Description: Redis 异常 + */ +public class RedisPluginException extends ServiceException { + + public RedisPluginException(Integer code, String errorMessage) { + super(code, errorMessage); + } + + public RedisPluginException(BaseMsg msg) { + super(msg); + } +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/msg/RedisMsg.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/msg/RedisMsg.java new file mode 100644 index 0000000..4d997ac --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/msg/RedisMsg.java @@ -0,0 +1,40 @@ +package org.opsli.plugins.redis.msg; + +import org.opsli.common.base.msg.BaseMsg; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.mail.msg + * @Author: Parker + * @CreateTime: 2020-09-13 19:54 + * @Description: Redis消息 + */ +public enum RedisMsg implements BaseMsg { + + /** Redis异常 */ + EXCEPTION_KEY_NULL(90100,"Key不可为空!"), + EXCEPTION_INCREMENT(90101,"递增值必须大于0!"), + EXCEPTION_DECREMENT(90102,"递减值必须大于0!"), + EXCEPTION_REFLEX(90103,"反射Redis脚本失败"), + EXCEPTION_PUSH_SUB_NULL(90104,"发布消息体不可为空!"), + ; + + + private int code; + private String message; + + RedisMsg(int code, String message){ + this.code = code; + this.message = message; + } + + @Override + public Integer getCode() { + return this.code; + } + + @Override + public String getMessage() { + return this.message; + } +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/BaseSubMessage.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/BaseSubMessage.java new file mode 100644 index 0000000..6b2705d --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/BaseSubMessage.java @@ -0,0 +1,34 @@ +package org.opsli.plugins.redis.pushsub.entity; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +public class BaseSubMessage implements RedisPushSubMessage{ + + public static final String BASE_TYPE = "TYPE"; + + /** 发布订阅频道名称 */ + protected String channel; + + protected String json; + + + /** + * 构造函数 转换json + * @param channel 通道 + * @param type 类型 + * @param jsonObj 数据 + */ + public void build(String channel, String type, JSONObject jsonObj) { + if(channel == null || type == null || jsonObj == null){ + return; + } + jsonObj.put(BASE_TYPE, type); + this.json = jsonObj.toString(); + this.channel = channel; + } + +} \ No newline at end of file diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/RedisPushSubMessage.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/RedisPushSubMessage.java new file mode 100644 index 0000000..17bf878 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/entity/RedisPushSubMessage.java @@ -0,0 +1,15 @@ +package org.opsli.plugins.redis.pushsub.entity; + +import java.io.Serializable; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.entity + * @Author: Parker + * @CreateTime: 2020-09-15 14:50 + * @Description: Redis 消息订阅 + */ +public interface RedisPushSubMessage extends Serializable { + + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java new file mode 100644 index 0000000..ad15441 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/pushsub/receiver/BaseReceiver.java @@ -0,0 +1,36 @@ +package org.opsli.plugins.redis.pushsub.receiver; + + +import com.alibaba.fastjson.JSONObject; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.receiver + * @Author: Parker + * @CreateTime: 2020-09-15 14:49 + * @Description: Redis 消息订阅实现基类 + */ +public abstract class BaseReceiver { + + public static final String BASE_CHANNEL = "listener:msg:"; + private String channel; + + public BaseReceiver(String channel){ + this.channel = BASE_CHANNEL+channel; + } + + /** + * 获得监听信道 + * @return + */ + public String getListenerChannel(){ + return this.channel; + } + + /** + * 获得消息 + * @param msg + */ + public abstract void receiveMessage(String msg); + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisLockScript.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisLockScript.java new file mode 100644 index 0000000..05ecd7e --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisLockScript.java @@ -0,0 +1,38 @@ +package org.opsli.plugins.redis.scripts; + + +import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.scripts + * @Author: Parker + * @CreateTime: 2020-09-14 22:01 + * @Description: Redis 锁 Lua脚本 + * + * Redis 集群下 ,尽量不要使用Redis做为分布式锁 + * + */ +public class RedisLockScript implements RedisPluginScript { + + @Override + public RedisScriptsEnum getEnum() { + return RedisScriptsEnum.REDIS_LOCK; + } + + @Override + public String getScript() { + return "local key = KEYS[1]\n" + + "local identifier = ARGV[1]\n" + + "local lockTimeOut = ARGV[2]\n" + + "\n" + + "if redis.call(\"SETNX\", key, identifier) == 1 then\n" + + " redis.call(\"EXPIRE\", key, lockTimeOut)\n" + + " return 1\n" + + "elseif redis.call(\"TTL\", key) == -1 then\n" + + " redis.call(\"EXPIRE\", key, lockTimeOut)\n" + + "end\n" + + "return 0"; + } + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisPluginScript.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisPluginScript.java new file mode 100644 index 0000000..49c8687 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisPluginScript.java @@ -0,0 +1,30 @@ +package org.opsli.plugins.redis.scripts; + + +import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.scripts + * @Author: Parker + * @CreateTime: 2020-09-14 22:05 + * @Description: Redis Lua 脚本 + * + * Lua 比 这些框架靠谱的多 , 单线程保障了Redis的原子性操作 + * + */ +public interface RedisPluginScript { + + /** + * 获得所属字典 + * @return + */ + RedisScriptsEnum getEnum(); + + /** + * 获得脚本 + * @return + */ + String getScript(); + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisScriptCache.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisScriptCache.java new file mode 100644 index 0000000..3bd6349 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisScriptCache.java @@ -0,0 +1,50 @@ +package org.opsli.plugins.redis.scripts; + +import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 脚本缓存 + * @author parker + */ +public class RedisScriptCache { + + /** 脚本存放容器 */ + private final ConcurrentMap scriptCacheMap = new ConcurrentHashMap<>(); + + /** + * 获得缓存脚本 + * @param scriptsEnum 脚本Enum + * @return 脚本 + */ + public RedisPluginScript getScript(RedisScriptsEnum scriptsEnum){ + if(scriptsEnum == null){ + return null; + } + return scriptCacheMap.get(scriptsEnum); + } + + /** + * 获得缓存脚本 + * @param redisPluginScript 脚本Enum + * @return 脚本 + */ + public boolean putScript(RedisPluginScript redisPluginScript){ + boolean ret = true; + if(redisPluginScript == null || redisPluginScript.getScript() == null || "".equals(redisPluginScript.getScript()) + || redisPluginScript.getEnum() == null + ){ + return false; + } + try { + scriptCacheMap.put(redisPluginScript.getEnum(),redisPluginScript); + } catch (Exception e) { + ret = false; + e.printStackTrace(); + } + return ret; + } + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisUnLockScript.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisUnLockScript.java new file mode 100644 index 0000000..c9341bb --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/RedisUnLockScript.java @@ -0,0 +1,35 @@ +package org.opsli.plugins.redis.scripts; + + +import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.scripts + * @Author: Parker + * @CreateTime: 2020-09-14 22:01 + * @Description: Redis 锁 Lua脚本 + * + * Redis 集群下 ,尽量不要使用Redis做为分布式锁 + * + */ +public class RedisUnLockScript implements RedisPluginScript { + + @Override + public RedisScriptsEnum getEnum() { + return RedisScriptsEnum.REDIS_UN_LOCK; + } + + @Override + public String getScript() { + return "local key = KEYS[1]\n" + + "local identifier = ARGV[1]\n" + + "\n" + + "if redis.call(\"GET\", key) == identifier then\n" + + " redis.call(\"DEL\", key)\n" + + " return 1\n" + + "end\n" + + "return 0"; + } + +} diff --git a/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/enums/RedisScriptsEnum.java b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/enums/RedisScriptsEnum.java new file mode 100644 index 0000000..49e2ee9 --- /dev/null +++ b/opsli-plugins/opsli-plugins-redis/src/main/java/org/opsli/plugins/redis/scripts/enums/RedisScriptsEnum.java @@ -0,0 +1,19 @@ +package org.opsli.plugins.redis.scripts.enums; + + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.plugins.redis.enums + * @Author: Parker + * @CreateTime: 2020-09-14 21:56 + * @Description: Redis 脚本枚举 + */ +public enum RedisScriptsEnum { + + /** Redis加锁脚本 */ + REDIS_LOCK, + /** Redis解锁脚本 */ + REDIS_UN_LOCK + ; + +}