Redis 分布式锁 优化 - 直接写lua脚本

v1.4.1
Parker 5 years ago
parent 0361bff1e3
commit 4e0eddf79e

1
.gitignore vendored

@ -32,3 +32,4 @@ build/
/git-pull.sh
/git-push.sh
/app-log/
/app-opsli-log/

@ -10,9 +10,9 @@ import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
import org.opsli.plugins.redis.pushsub.receiver.BaseReceiver;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @BelongsProject: opsli-boot
@ -31,7 +31,7 @@ public class RedisPushSubReceiver extends BaseReceiver {
public static final String CHANNEL = "opsli";
/** 处理方法集合 */
private static final Map<PushSubType, RedisPushSubHandler> HANDLER_MAP = new HashMap<>();
private static final ConcurrentMap<PushSubType, RedisPushSubHandler> HANDLER_MAP = new ConcurrentHashMap<>();
static {
// 拿到state包下 实现了 SystemEventState 接口的,所有子类

@ -1,12 +1,12 @@
package org.opsli.modulars.test.web;
import cn.hutool.core.thread.ThreadUtil;
import org.apache.commons.lang3.StringUtils;
import org.opsli.common.api.ResultVo;
import org.opsli.common.base.concroller.BaseController;
import org.opsli.core.cache.pushsub.msgs.DictMsgFactory;
import org.opsli.plugins.mail.MailPlugin;
import org.opsli.plugins.mail.model.MailModel;
import org.opsli.plugins.redis.RedisLockPlugins;
import org.opsli.plugins.redis.RedisPlugin;
import org.opsli.plugins.redis.lock.RedisLock;
import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage;
@ -34,6 +34,9 @@ public class TestRestController extends BaseController {
@Autowired
private RedisPlugin redisPlugin;
@Autowired
private RedisLockPlugins redisLockPlugins;
@GetMapping("/sendMail")
public ResultVo sendMail(){
MailModel mailModel = new MailModel();
@ -88,24 +91,24 @@ public class TestRestController extends BaseController {
// 锁凭证 redisLock 贯穿全程
RedisLock redisLock = new RedisLock();
redisLock.setLockName("aaabbb");
redisLock.setAcquireTimeOut(2000L);
redisLock.setLockTimeOut(10000L);
redisLock.setLockName("aaabbb")
.setAcquireTimeOut(2000L)
.setLockTimeOut(10000L);
redisLock = redisPlugin.tryLock(redisLock);
redisLock = redisLockPlugins.tryLock(redisLock);
if(redisLock == null){
ResultVo error = ResultVo.error("获得锁失败!!!!!!");
error.put("identifier",redisLock);
error.put("redisLock",redisLock);
return error;
}
// 睡眠
// 睡眠 模拟线程执行过程
ThreadUtil.sleep(60, TimeUnit.SECONDS);
redisPlugin.unLock(redisLock);
redisLockPlugins.unLock(redisLock);
ResultVo success = ResultVo.success("获得锁成功!!!!!!");
success.put("identifier",redisLock);
success.put("redisLock",redisLock);
return success;
}

@ -0,0 +1,28 @@
package org.opsli.plugins.redis;
import org.opsli.plugins.redis.lock.RedisLock;
/**
* @BelongsProject: opsli-boot
* @BelongsPackage: org.opsli.plugins.redis
* @Author: Parker
* @CreateTime: 2020-09-16 11:47
* @Description: Redis
*/
public interface RedisLockPlugins {
/**
*
* @param redisLock redis
* @return
*/
RedisLock tryLock(RedisLock redisLock);
/**
*
* @param redisLock redis
* @return
*/
boolean unLock(RedisLock redisLock);
}

@ -1,28 +1,23 @@
package org.opsli.plugins.redis;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.opsli.plugins.redis.exception.RedisPluginException;
import org.opsli.plugins.redis.lock.RedisLock;
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.core.io.ClassPathResource;
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.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.FutureTask;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@ -93,153 +88,18 @@ public class RedisPlugin {
*/
public Object callScript(RedisScriptsEnum scriptsEnum, List<String> keys, Object... argv) {
// 获得Script脚本
RedisPluginScript script = redisScriptCache.getScript(scriptsEnum);
if(script == null){
String script = redisScriptCache.getScript(scriptsEnum);
if(script == null || "".equals(script)){
return false;
}
// 这里有坑 DefaultRedisScript 必须传 ResultType 类型 且为 Long类型否则报错
DefaultRedisScript<Long> lockScript = new DefaultRedisScript<>();
lockScript.setScriptText(script.getScript());
lockScript.setScriptText(script);
lockScript.setResultType(Long.class);
return redisTemplate.execute(lockScript, keys, argv);
}
// ===================== Redis 锁相关 =====================
/*
* Redis使
*
* RedisLock redisLock = new RedisLock() 穿
* redisPlugin.tryLock(redisLock)
* redisPlugin.unLock(redisLock)
*
*
* 1tair线
* 2
* 3线使keytairput
* */
/**
* Redis
* @param redisLock
* @return identifier
*/
public RedisLock tryLock(RedisLock redisLock) {
// 锁凭证
String identifier = UUID.randomUUID().toString().replaceAll("-","");
redisLock = this.tryLock(redisLock,identifier);
if(redisLock != null){
log.info("分布式锁 - 开启: 锁名称: "+redisLock.getLockName()+" 锁凭证: \""+redisLock.getIdentifier()+"\"");
// 启动看门狗
this.lockDog(redisLock);
}
return redisLock;
}
/**
* Redis
* @param redisLock
* @return identifier
*/
private RedisLock tryLock(RedisLock redisLock,String identifier) {
try {
List<String> keys = Collections.singletonList("lock:" + redisLock.getLockName());
long acquireTimeEnd = System.currentTimeMillis() + redisLock.getAcquireTimeOut();
boolean acquired = false;
// 尝试获得锁
while (!acquired && (System.currentTimeMillis() < acquireTimeEnd)) {
Long ret = (Long) this.callScript(RedisScriptsEnum.REDIS_LOCK, keys, identifier,redisLock.getLockTimeOut());
if(ret == null){
break;
}
if (1 == ret){
acquired = true;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {
log.error(ignored.getMessage(),ignored);
}
}
}
redisLock.setIdentifier(identifier);
return acquired ? redisLock : null;
}catch (Exception e){
log.error(e.getMessage(),e);
return null;
}
}
/**
* Redis
* @param redisLock
* @return boolean
*/
public boolean unLock(RedisLock redisLock) {
try {
List<String> keys = Collections.singletonList("lock:" + redisLock.getLockName());
Long ret = (Long) this.callScript(RedisScriptsEnum.REDIS_UN_LOCK, keys, redisLock.getIdentifier());
// 减去线程锁
redisLock.unThreadLock();
log.info("分布式锁 - 解除: 锁名称: "+redisLock.getLockName()+" 锁凭证: "+redisLock.getIdentifier());
if(ret == null){
return false;
}
if(1 == ret){
return true;
}
return false;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Redis - 使
* @param redisLock
* @return boolean
*/
private void lockDog(RedisLock redisLock) {
Thread t = new Thread(()->{
try {
// 倒计时前续命
long countDownTime = 3000L;
// 锁释放时间
long lockTimeOutEnd = System.currentTimeMillis() + redisLock.getLockTimeOut();
boolean dogFlag = true;
// 看门狗检测 当前线程是否还存活
while (dogFlag) {
int lock = redisLock.getThreadLock();
if(lock <= 0){
dogFlag = false;
// 再一次确定 解锁 防止线程差 最后加锁
this.unLock(redisLock);
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {
log.error(ignored.getMessage(),ignored);
}
// 如果 距离倒计时 前 2000 毫秒还没有动作 则执行续命
if((System.currentTimeMillis()+countDownTime) >= lockTimeOutEnd){
Object o = this.tryLock(redisLock,redisLock.getIdentifier());
if(o != null){
lockTimeOutEnd = System.currentTimeMillis() + redisLock.getLockTimeOut();
log.info("分布式锁 - 续命: 锁名称: "+redisLock.getLockName()+" 锁凭证: "+redisLock.getIdentifier());
}
}
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
});
t.setName("分布式锁看门狗:"+" 锁名称: "+redisLock.getLockName()+" 锁凭证: "+redisLock.getIdentifier());
t.start();
}
// ===================== 缓存有效时间相关 =====================
/**

@ -1,25 +1,19 @@
package org.opsli.plugins.redis.conf;
import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
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.opsli.plugins.redis.scripts.enums.RedisScriptsEnum;
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.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.io.BufferedReader;
import java.io.InputStream;
/**
* @Author parker
@ -71,31 +65,56 @@ public class RedisPluginConfig {
*/
@Bean
public RedisScriptCache loadScripts() {
RedisScriptCache redisScriptCache = new RedisScriptCache();
// 拿到state包下 实现了 SystemEventState 接口的,所有子类
Set<Class<?>> clazzSet = PackageUtil.listSubClazz(RedisPluginScript.class.getPackage().getName(),
true,
RedisPluginScript.class
);
for (Class<?> aClass : clazzSet) {
// 位运算 去除抽象类
if((aClass.getModifiers() & Modifier.ABSTRACT) != 0){
continue;
}
// 通过反射 加载所有的 脚本
RedisScriptsEnum[] scriptEnums = RedisScriptsEnum.values();
for (RedisScriptsEnum scriptEnum : scriptEnums) {
String path = scriptEnum.getPath();
InputStream resourceAsStream = null;
try {
RedisPluginScript redisPluginScript = (RedisPluginScript) aClass.newInstance();
redisScriptCache.putScript(redisPluginScript);
} catch (Exception e) {
log.error(RedisMsg.EXCEPTION_REFLEX.getMessage());
// IO流 读取lua脚本 存入缓存当中 提高访问效率 避免每次使用脚本还需要再开启IO流
StringBuffer stb = new StringBuffer();
resourceAsStream= RedisPluginConfig.class.getResourceAsStream(path);
BufferedReader br = IoUtil.getUtf8Reader(resourceAsStream);
String line = null;
while((line = br.readLine())!=null){
stb.append(line);
stb.append("\n");
}
// 保存脚本到缓存中
redisScriptCache.putScript(scriptEnum,stb.toString());
br.close();
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
IoUtil.close(resourceAsStream);
}
}
//
// // 拿到state包下 实现了 SystemEventState 接口的,所有子类
// Set<Class<?>> 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;
}

@ -1,11 +0,0 @@
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 {
}

@ -1,7 +1,5 @@
package org.opsli.plugins.redis.lock;
import lombok.Data;
import lombok.Value;
import java.util.concurrent.atomic.AtomicInteger;
@ -12,7 +10,6 @@ import java.util.concurrent.atomic.AtomicInteger;
* @CreateTime: 2020-09-16 00:51
* @Description: Redis
*/
@Data
public class RedisLock {
/** 锁名称 */
@ -28,16 +25,78 @@ public class RedisLock {
private String identifier;
/** 线程锁 */
private AtomicInteger atomicInteger = new AtomicInteger(1);
private AtomicInteger atomicInteger;
/**
*
*/
public RedisLock() {
// 初始化锁
atomicInteger = new AtomicInteger(1);
}
/**
*
*/
public RedisLock(String lockName, Long acquireTimeOut, Long lockTimeOut, String identifier) {
this.lockName = lockName;
this.acquireTimeOut = acquireTimeOut;
this.lockTimeOut = lockTimeOut;
this.identifier = identifier;
// 初始化锁
atomicInteger = new AtomicInteger(1);
}
/** 获得线程锁 */
public int getThreadLock(){
public int threadGetLock(){
return atomicInteger.get();
}
/** 解除线程锁 */
public int unThreadLock(){
public int threadUnLock(){
return atomicInteger.decrementAndGet();
}
// ==========================================================
public String getLockName() {
return lockName;
}
public RedisLock setLockName(String lockName) {
this.lockName = lockName;
return this;
}
public Long getAcquireTimeOut() {
return acquireTimeOut;
}
public RedisLock setAcquireTimeOut(Long acquireTimeOut) {
this.acquireTimeOut = acquireTimeOut;
return this;
}
public Long getLockTimeOut() {
return lockTimeOut;
}
public RedisLock setLockTimeOut(Long lockTimeOut) {
this.lockTimeOut = lockTimeOut;
return this;
}
public String getIdentifier() {
return identifier;
}
public RedisLock setIdentifier(String identifier) {
this.identifier = identifier;
return this;
}
}

@ -0,0 +1,180 @@
package org.opsli.plugins.redis.lock;
import lombok.extern.slf4j.Slf4j;
import org.opsli.plugins.redis.RedisLockPlugins;
import org.opsli.plugins.redis.RedisPlugin;
import org.opsli.plugins.redis.scripts.enums.RedisScriptsEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
* @BelongsProject: opsli-boot
* @BelongsPackage: org.opsli.plugins.redis.lock
* @Author: Parker
* @CreateTime: 2020-09-16 11:49
* @Description: Redis
*
* ===================== Redis =====================
*
* Redis使
*
* RedisLock redisLock = new RedisLock() 穿
* redisPlugin.tryLock(redisLock)
* redisPlugin.unLock(redisLock)
*
*
* 1tair线
* 2
* 3线使keytairput
*
*/
@Slf4j
@Service
public class RedisLockImpl implements RedisLockPlugins {
/** 锁前缀 */
private static final String LOCK_NAME_PREFIX = "lock:";
@Autowired
private RedisPlugin redisPlugin;
/**
* Redis
* @param redisLock
* @return identifier
*/
@Override
public RedisLock tryLock(RedisLock redisLock) {
// 锁凭证
String identifier = UUID.randomUUID().toString().replaceAll("-","");
redisLock = this.tryLock(redisLock,identifier);
if(redisLock != null){
log.info(this.getInfo("分布式锁 - 开启",redisLock));
// 启动看门狗
this.lockDog(redisLock);
}
return redisLock;
}
/**
* Redis
* @param redisLock
* @return boolean
*/
@Override
public boolean unLock(RedisLock redisLock) {
try {
List<String> keys = Collections.singletonList(LOCK_NAME_PREFIX + redisLock.getLockName());
Long ret = (Long) redisPlugin.callScript(RedisScriptsEnum.REDIS_UN_LOCK, keys,
redisLock.getIdentifier());
// 减去线程锁
redisLock.threadUnLock();
log.info(this.getInfo("分布式锁 - 解除",redisLock));
if(ret == null){
return false;
}
if(1 == ret){
return true;
}
return false;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Redis
* @param redisLock
* @return identifier
*/
private RedisLock tryLock(RedisLock redisLock,String identifier) {
try {
List<String> keys = Collections.singletonList("lock:" + redisLock.getLockName());
long acquireTimeEnd = System.currentTimeMillis() + redisLock.getAcquireTimeOut();
boolean acquired = false;
// 尝试获得锁
while (!acquired && (System.currentTimeMillis() < acquireTimeEnd)) {
Long ret = (Long) redisPlugin.callScript(RedisScriptsEnum.REDIS_LOCK, keys,
identifier,redisLock.getLockTimeOut());
if(ret == null){
break;
}
if (1 == ret){
acquired = true;
} else {
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {
log.error(ignored.getMessage(),ignored);
}
}
}
redisLock.setIdentifier(identifier);
return acquired ? redisLock : null;
}catch (Exception e){
log.error(e.getMessage(),e);
return null;
}
}
/**
* Redis - 使
* @param redisLock
* @return boolean
*/
private void lockDog(RedisLock redisLock) {
Thread t = new Thread(()->{
try {
// 倒计时前续命
long countDownTime = 3000L;
// 锁释放时间
long lockTimeOutEnd = System.currentTimeMillis() + redisLock.getLockTimeOut();
boolean dogFlag = true;
// 看门狗检测 当前线程是否还存活
while (dogFlag) {
int lock = redisLock.threadGetLock();
if(lock <= 0){
dogFlag = false;
// 再一次确定 解锁 防止线程差 最后加锁
this.unLock(redisLock);
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException ignored) {
log.error(ignored.getMessage(),ignored);
}
// 如果 距离倒计时 前 2000 毫秒还没有动作 则执行续命
if((System.currentTimeMillis()+countDownTime) >= lockTimeOutEnd){
Object o = this.tryLock(redisLock,redisLock.getIdentifier());
if(o != null){
lockTimeOutEnd = System.currentTimeMillis() + redisLock.getLockTimeOut();
log.info(this.getInfo("分布式锁 - 续命",redisLock));
}
}
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
});
t.setName(this.getInfo("分布式锁看门狗",redisLock));
t.start();
}
/**
*
* @param name
* @param redisLock
* @return String
*/
private String getInfo(String name,RedisLock redisLock){
return name + " 锁名称: "+redisLock.getLockName()+" 锁凭证: "+redisLock.getIdentifier();
}
}

@ -1,47 +0,0 @@
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 "-- 加锁脚本\n"+
"-- key1要加锁的名称 argv1:当前线程或主机的地址 argv2锁存活的时间ms \n"+
"local expire_time = tonumber(ARGV[2])\n"+
"if redis.call('exists', KEYS[1]) == 0 then\n"+
" -- 锁不存在创建一把锁存入hash类型的值\n"+
" redis.call('hset', KEYS[1], ARGV[1], 1)\n"+
" -- 设置锁的存活时间,防止死锁\n"+
" redis.call('pexpire', KEYS[1], expire_time)\n"+
" return 1\n"+
"end\n"+
"if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then\n"+
" -- 表示是同一线程重入\n"+
" redis.call('hincrby', KEYS[1], ARGV[1], 1)\n"+
" -- 重新设置锁的过期时间\n"+
" redis.call('pexpire', KEYS[1], expire_time)\n"+
" return 1\n"+
"end\n"+
"-- 没抢到锁返回锁的剩余有效时间ms\n"+
"return 0\n";
}
}

@ -1,30 +0,0 @@
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();
}

@ -12,14 +12,14 @@ import java.util.concurrent.ConcurrentMap;
public class RedisScriptCache {
/** 脚本存放容器 */
private final ConcurrentMap<RedisScriptsEnum, RedisPluginScript> scriptCacheMap = new ConcurrentHashMap<>();
private final ConcurrentMap<RedisScriptsEnum, String> scriptCacheMap = new ConcurrentHashMap<>();
/**
*
* @param scriptsEnum Enum
* @return
*/
public RedisPluginScript getScript(RedisScriptsEnum scriptsEnum){
public String getScript(RedisScriptsEnum scriptsEnum){
if(scriptsEnum == null){
return null;
}
@ -28,18 +28,18 @@ public class RedisScriptCache {
/**
*
* @param redisPluginScript Enum
* @param scriptsEnum Enum
* @param script
* @return
*/
public boolean putScript(RedisPluginScript redisPluginScript){
public boolean putScript(RedisScriptsEnum scriptsEnum, String script){
boolean ret = true;
if(redisPluginScript == null || redisPluginScript.getScript() == null || "".equals(redisPluginScript.getScript())
|| redisPluginScript.getEnum() == null
if(scriptsEnum == null || script == null || "".equals(script)
){
return false;
}
try {
scriptCacheMap.put(redisPluginScript.getEnum(),redisPluginScript);
scriptCacheMap.put(scriptsEnum,script);
} catch (Exception e) {
ret = false;
e.printStackTrace();

@ -1,43 +0,0 @@
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 "-- 解锁脚本\n"+
"-- 判断是当前线程持有锁,避免解了其他线程加的锁\n"+
"if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then\n"+
" -- 重入次数大于1扣减次数\n"+
" --if tonumber(redis.call('hget',KEYS[1],ARGV[1])) > 1 then\n"+
" -- return redis.call('hincrby', KEYS[1], ARGV[1], -1);\n"+
" -- 重入次数等于1删除该锁\n"+
" --else\n"+
" redis.call('del', KEYS[1]);\n"+
" return 1;\n"+
" --end\n"+
"-- 判断不是当前线程持有锁,返回解锁失败\n"+
"else\n"+
" return 0;\n"+
"end\n";
}
}

@ -11,9 +11,23 @@ package org.opsli.plugins.redis.scripts.enums;
public enum RedisScriptsEnum {
/** Redis加锁脚本 */
REDIS_LOCK,
REDIS_LOCK("/lua/redis_lock.lua"),
/** Redis解锁脚本 */
REDIS_UN_LOCK
REDIS_UN_LOCK("/lua/redis_unlock.lua")
;
/** 脚本路径 */
private String path;
RedisScriptsEnum(String path){
this.path = path;
}
/**
*
* @return path
*/
public String getPath() {
return path;
}
}

@ -0,0 +1,19 @@
-- 加锁脚本
-- key1要加锁的名称 argv1:当前线程或主机的地址 argv2锁存活的时间ms
local expire_time = tonumber(ARGV[2])
if redis.call('exists', KEYS[1]) == 0 then
-- 锁不存在创建一把锁存入hash类型的值
redis.call('hset', KEYS[1], ARGV[1], 1)
-- 设置锁的存活时间,防止死锁
redis.call('pexpire', KEYS[1], expire_time)
return 1
end
if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then
-- 表示是同一线程重入
redis.call('hincrby', KEYS[1], ARGV[1], 1)
-- 重新设置锁的过期时间
redis.call('pexpire', KEYS[1], expire_time)
return 1
end
-- 没抢到锁返回锁的剩余有效时间ms
return 0

@ -0,0 +1,15 @@
-- 解锁脚本
-- 判断是当前线程持有锁,避免解了其他线程加的锁
if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then
-- 重入次数大于1扣减次数
--if tonumber(redis.call('hget',KEYS[1],ARGV[1])) > 1 then
-- return redis.call('hincrby', KEYS[1], ARGV[1], -1)
-- 重入次数等于1删除该锁
--else
redis.call('del', KEYS[1]);
return 1
--end
-- 判断不是当前线程持有锁,返回解锁失败
else
return 0
end
Loading…
Cancel
Save