分布式锁-lua脚本
// 工具类
@Component public class RedisLock { @Autowired private RedisTemplate redisTemplate; // 时间轮异步定时执行 private HashedWheelTimer timer = new HashedWheelTimer(); private DefaultRedisScript addTimeScript; { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(Long.class); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_lock.lua"))); this.addTimeScript = redisScript; } /*** * @Description: 获取锁并设置过期时间 * @Author: szc * @Date: 2023/8/15 19:52 * @Params [key, expireSecond] * @Return boolean */ public boolean tryLock(String key, long expireSecond){ DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(Long.class); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_lock.lua"))); Long isSuccess = (Long) redisTemplate.execute(redisScript, Arrays.asList(key), expireSecond); return isSuccess != null && isSuccess == 1L; } /*** * @Description: 释放锁,删除key * @Author: szc * @Date: 2023/8/15 19:52 * @Params [key, expireSecond] * @Return boolean */ public boolean unLock(String key){ DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(Long.class); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_unlock.lua"))); Long isSuccess = (Long) redisTemplate.execute(redisScript, Arrays.asList(key)); return isSuccess != null && isSuccess == 1L; } /*** * @Description: 自增1 * @Author: szc * @Date: 2023/8/15 19:52 * @Params [key, expireSecond] * @Return boolean */ public boolean incrKey(String key, long expireSecond){ DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setResultType(Long.class); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/redis_incr_expire.lua"))); Long isSuccess = (Long) redisTemplate.execute(redisScript, Arrays.asList(key), expireSecond); Object num = redisTemplate.opsForValue().get(key); System.out.println("incrKey========="+num); return isSuccess != null && isSuccess == 1L; } /*** * @Description : key过期前未执行完,自动续期,增加过期时间 * @Author: szc * @Date: 2023/8/15 22:07 * @Params [key, time] * @Return void */ public void addTime(String key, long time) { timer.newTimeout( timerTask -> { //意思就是执行luaaa,key给KEYS[1],time给ARGV[1] Long execute = (Long) redisTemplate.execute(addTimeScript, Arrays.asList(key), time); if (execute == 0) { ThreadUtils.threadInfo("当前key还存在,自动续期。"); addTime(key, time); } else { ThreadUtils.threadInfo("key不存在了,不需要续期"); } }, time / 3, TimeUnit.SECONDS);//time的三分之一时间去执行看有没有key,有重置过期时间,没有就什么都不做。 } }
src\main\resources\redis\redis_incr_expire.lua
-- 每次增加1,设置过期时间
local result = tonumber(redis.call('incr', KEYS[1]))
if(result == 1) then
redis.call('expire', KEYS[1], ARGV[1])
return result
else
return result
end
src\main\resources\redis\redis_lock.lua
-- 使用中的坑:需要重写redistemplate , 不然返回的都是0
-- eval "return tonumber(redis.call('setnx', 'lock_test', 'lock_test'))" 0 对应下一行执行
local result = tonumber(redis.call('setnx', KEYS[1], ARGV[1]))
if(result == 1) then
redis.call('expire', KEYS[1], ARGV[1])
return result
else
return result
end
\src\main\resources\redis\redis_unlock.lua
-- 15 为设置的过期时间,可以java参数传递,使用ARGV[1]接收
if(redis.call('get', KEYS[1]) == '15') then
return redis.call('del', KEYS[1])
else
return 0
end
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yml
spring:
redis:
host: localhost
port: 6379
password:
使用
public class RedisLockController {
@Autowired
private RedisLock redisLock;
@RequestMapping("lock")
public void lock(){
long expireSecond = 15;
String key = "lock_test";
boolean lock = redisLock.tryLock(key, expireSecond);
if(lock){
try {
// key过期前未执行完,自动续期,增加过期时间
redisLock.addTime(key,expireSecond);
log.info("获取锁成功。。。。。。");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
boolean unLock = redisLock.unLock(key);
if(unLock){
log.info("释放锁成功!");
}else {
log.info("释放锁失败!");
}
}
}else {
log.info("获取锁失败。");
}
}
@RequestMapping("incrKey")
public void incrKey(){
redisLock.incrKey("num_test", 60);
}
浙公网安备 33010602011771号