分布式锁-lua脚本

发布时间 2023-08-18 16:38:45作者: 我没有出家
// 工具类
@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);
    }