package com.first.core.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
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.Collections;
import java.util.List;
import java.util.Objects;
/**
* @author :lizn
* @description:TODO
* @date :2022-06-15
*/
@Slf4j
public class DistributeRedisLock {
//上锁脚本
private static final String LOCK_LUA = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1], ARGV[2]) return 'true' else return 'false' end";
//解锁脚本
private static final String UNLOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) end return 'true' ";
public static final int DEFAULT_SECOND_LEN = 10; // 10 s
private static RedisSerializer<String> argsSerializer = new StringRedisSerializer();
private static RedisSerializer<String> resultSerializer = new StringRedisSerializer();
/**
* @Date 2020/7/11 21:52
* @Author lizn
* @Description
* @Param [lockKey 锁住的key, lockExpireMils 锁住的时长。如果超时未解锁,视为加锁线程死亡,其他线程可夺取锁]
* @Return boolean
*/
public static boolean lock(RedisTemplate redisTemplate,String lockKey, long lockExpireMils) {
return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
long nowTime = System.currentTimeMillis();
Boolean acquire = connection.setNX(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
if (acquire) {
return Boolean.TRUE;
} else {
byte[] value = connection.get(lockKey.getBytes());
if (Objects.nonNull(value) && value.length > 0) {
long oldTime = Long.parseLong(new String(value));
if (oldTime < nowTime) {
//connection.getSet:返回这个key的旧值并设置新值。
byte[] oldValue = connection.getSet(lockKey.getBytes(), String.valueOf(nowTime + lockExpireMils + 1).getBytes());
//当key不存时会返回空,表示key不存在或者已在管道中使用
return oldValue == null ? false : Long.parseLong(new String(oldValue)) < nowTime;
}
}
}
return Boolean.FALSE;
});
}
/**
* @Date 2020/7/11 8:52
* @Author lizn
* @Description 加锁,默认锁的过期时间为10s
* @Param [lockKey锁住的key, val key值]
* @Return boolean
*/
public static boolean lock(RedisTemplate redisTemplate,String lockKey, String val) {
return lock(redisTemplate,lockKey, val, DEFAULT_SECOND_LEN);
}
/**
* @Date 2020/7/11 8:53
* @Author lizn
* @Description 加锁
* @Param [lockKey锁住的key, val key值,确保唯一, second 过期时间,防止死锁]
* @Return boolean
*/
public static boolean lock(RedisTemplate redisTemplate,String lockKey, String val, int second) {
RedisScript lockRedisScript = RedisScript.of(LOCK_LUA, String.class);
List<String> keys = Collections.singletonList(lockKey);
Object flag = redisTemplate.execute(lockRedisScript, argsSerializer, resultSerializer, keys, val, String.valueOf(second));
return Boolean.valueOf(flag.toString());
}
/**
* @Date 2020/7/11 8:53
* @Author lizn
* @Description 通过key和val值解锁,只有满足key值和val值相等才能解锁,防止被其他线程并发解锁
* @Param [lockKey 被锁的key, val key值]
* @Return void
*/
public static void unlock(RedisTemplate redisTemplate,String lockKey, String val) {
RedisScript unLockRedisScript = RedisScript.of(UNLOCK_LUA, String.class);
List<String> keys = Collections.singletonList(lockKey);
redisTemplate.execute(unLockRedisScript, argsSerializer, resultSerializer, keys, val);
}
}