package org.sxkj.common.redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* redis 集群共享锁
*
* @author raojiangang
*/
public class RedisLockUtils
{
private static final Logger LOG = LoggerFactory.getLogger(RedisLockUtils.class);
/**
* 加锁超时时间,单位毫秒,
* 30秒超时
*/
private static final long LOCK_TIMEOUT = 30 * 1000;
private RedisTemplate primaryStringRedisTemplate;
public RedisLockUtils(RedisTemplate redisTemplate) {
this.primaryStringRedisTemplate = redisTemplate;
}
/**
* 加锁
* 取到锁加锁,取不到锁一直等待知道获得锁
*
* @param lockKey key
* @return 时间毫秒
*/
private synchronized long lock(String lockKey) {
while (true) {
//锁时间
long lockTimeout = currentTimeForRedis() + LOCK_TIMEOUT + 1;
if (serializerRedisKey(lockKey, lockTimeout)) {
//设置超时时间,释放内存
primaryStringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
return lockTimeout;
} else {
//获取redis里面的时间
Object result = primaryStringRedisTemplate.opsForValue().get(lockKey);
Long currLockTimeoutStr = result == null ? null : Long.parseLong(result.toString());
//锁已经失效
if (currLockTimeoutStr != null && currLockTimeoutStr < System.currentTimeMillis()) {
Object andSet = primaryStringRedisTemplate.opsForValue().getAndSet(lockKey, Long.toString(lockTimeout));
Long oldLockTimeoutStr = Long.valueOf(
Objects.requireNonNull(
andSet.toString())
);
if (oldLockTimeoutStr.equals(currLockTimeoutStr)) {
primaryStringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
return lockTimeout;
}
}
}
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Boolean serializerRedisKey(String lockKey, Long lockTimeout) {
return primaryStringRedisTemplate.execute((RedisCallback) redisConnection -> {
//定义序列化方式
RedisSerializer serializer = primaryStringRedisTemplate.getStringSerializer();
byte[] value = serializer.serialize(lockTimeout.toString());
return (boolean) redisConnection.setNX(lockKey.getBytes(), value);
});
}
/**
* 解锁
*
* @param lockKey key
* @param lockValue value
*/
private synchronized void unlock(String lockKey, long lockValue) {
LOG.info("==========执行解锁==========");
//获取redis中设置的时间
Object result = primaryStringRedisTemplate.opsForValue().get(lockKey);
Long currentLockTimeoutStr = result == null ? null : Long.valueOf(result.toString());
//如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁
if (currentLockTimeoutStr != null && currentLockTimeoutStr == lockValue) {
primaryStringRedisTemplate.delete(lockKey);
LOG.info("==========解锁成功==========");
}
}
/**
* 获取服务时间
*
* @return 时间戳
*/
private long currentTimeForRedis() {
return primaryStringRedisTemplate.execute(
(RedisCallback) redisConnection -> redisConnection.time());
}
/**
* redis 全局锁
* 有参有返回值
*
* @param lockKey key
* @param function fun
*/
public R tryLock(String lockKey, P param, Function function) {
R result = null;
try {
long lockTime = lock(lockKey);
result = function.apply(param);
//释放锁
unlock(lockKey, lockTime);
} catch (Exception e) {
throw e;
} finally {
deleteKey(lockKey);
}
return result;
}
/**
* redis 全局锁
* 有参有返回值
*
* @param lockKey key
* @param function fun
*/
public R tryLock(String lockKey, Supplier function) {
R result = null;
try {
long lockTime = lock(lockKey);
result = function.get();
//释放锁
unlock(lockKey, lockTime);
} catch (Exception e) {
throw e;
} finally {
deleteKey(lockKey);
}
return result;
}
/**
* redis 全局锁
* 有参 无返回值
*
* @param lockKey key
* @param consumer fun
*/
public void tryLock(String lockKey, P param, Consumer consumer) {
try {
long lockTime = lock(lockKey);
//执行
consumer.accept(param);
//释放锁
unlock(lockKey, lockTime);
} finally {
deleteKey(lockKey);
}
}
/**
* redis 全局锁
* 无参无返回值
*
* @param lockKey key
* @param runnable fun
*/
public void tryLock(String lockKey, Runnable runnable) {
try {
long lockTime = lock(lockKey);
//执行
runnable.run();
//释放锁
unlock(lockKey, lockTime);
} finally {
deleteKey(lockKey);
}
}
private void deleteKey(String lockKey) {
primaryStringRedisTemplate.delete(lockKey);
}
}