package com.wtoip.yuncore.configuration.redis;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.DigestUtils;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class SimpleRedisLock
{
public static final String LOCK_PREFIX = "YUN:CORE:LOCK:";
private static final long MAX_LOCK_TIME_OUT_SECOND = 60L; // 默认1分钟
private static final String DEFAULT_LOCK_VALUE = "lock";
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleRedisLock.class);
private final StringRedisTemplate redisTemplate;
private final String lockKey;
private final byte[] lockKeyBytes;
private final byte[] lockValBytes;
public SimpleRedisLock(StringRedisTemplate redisTemplate, String lockKey)
{
this.redisTemplate = redisTemplate;
this.lockKey = lockKey;
this.lockKeyBytes = redisTemplate.getStringSerializer().serialize(lockKey);
this.lockValBytes = redisTemplate.getStringSerializer().serialize(DEFAULT_LOCK_VALUE);
}
public static String generateJsonMd5Key(Object obj)
{
return DigestUtils.md5DigestAsHex(JSON.toJSONBytes(obj));
}
public void lock()
{
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.lock. key="+lockKey);
}
redisTemplate.execute( (RedisCallback<String>) connection -> {
boolean acquired = connection.setNX(lockKeyBytes, lockValBytes);
connection.expire(lockKeyBytes, MAX_LOCK_TIME_OUT_SECOND);
while (!acquired) {
sleep(Long.valueOf(new Random().nextInt(200)), TimeUnit.MILLISECONDS);
acquired = connection.setNX(lockKeyBytes, lockValBytes);
}
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.lock. key="+lockKey+", release acquired");
}
return null;
},true);
}
public void lock(long time, TimeUnit unit)
{
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.lock("+time+","+unit+"). key="+lockKey);
}
long nanoTime = System.nanoTime();
long timeout = unit.toNanos(time);
redisTemplate.execute( (RedisCallback<String>) connection -> {
boolean acquired = connection.setNX(lockKeyBytes, lockValBytes);
if(acquired) {
connection.expire(lockKeyBytes, unit.toSeconds(time));
return null;
}
for ( ;; ){
if((System.nanoTime() - nanoTime) > timeout){ break;}
if(acquired) {
connection.expire(lockKeyBytes, unit.toSeconds(time));
break;
}
sleep(Long.valueOf(new Random().nextInt(100)), TimeUnit.MILLISECONDS);
acquired = connection.setNX(lockKeyBytes, lockValBytes);
}
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.lock("+time+","+unit+"). key="+lockKey+", release acquired");
}
return null;
}, true);
}
public boolean tryLock()
{
return tryLockExpireAfter(MAX_LOCK_TIME_OUT_SECOND, TimeUnit.SECONDS);
}
public boolean tryLockExpireAfter(long duration, TimeUnit unit)
{
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.tryLockExpireAfter, key="+lockKey+", will expire after " + unit.toSeconds(duration)+" seconds");
}
boolean acquired = redisTemplate.execute((RedisCallback<Boolean>) connection->{
boolean result = connection.setNX(lockKeyBytes, lockValBytes);
if(result) {
connection.expire(lockKeyBytes, unit.toSeconds(duration));
}
if(LOGGER.isInfoEnabled()){
LOGGER.info("SimpleRedisLock.tryLockExpireAfter. key="+lockKey+", result="+result);
}
return result;
},true);
return acquired;
}
public void unlock()
{
redisTemplate.execute((RedisCallback<String>) connection ->
{
connection.expire(lockKeyBytes, 0);
return null;
},true);
if (LOGGER.isInfoEnabled())
{
LOGGER.info("SimpleRedisLock.unlock key=" + lockKey + "");
}
}
public void unlockQuite()
{
try
{
unlock();
}catch (Exception e)
{
LOGGER.error("SimpleRedisLock.unLock cause error."+e.getMessage(), e);
}
}
private void sleep(long timeout, TimeUnit timeUnit)
{
try
{
timeUnit.sleep(timeout);
}
catch (InterruptedException e)
{
throw new IllegalStateException("获取锁线程被终端");
}
}
}