发布于 4年前
Redis分布式锁实现类
package com.dami.util.redis;
import java.util.Collections;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.Jedis;
/**
* Redis分布式锁实现类
* @ClassName: RedisDistributedLock
* @Description: TODO(这里用一句话描述这个类的作用)
* @author Administrator
* @date 2018-11-22 下午2:11:07
*/
public class RedisDistributedLock{
private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);
// 锁标志
public static final String PREFIX = "lock_";
// SET_IF_NOT_EXIST 标志,表示如果当key不存在时进行set操作,否则不处理
private static final String NX = "NX";
// 过期时间标志, EX 表示以秒作为单位
private static final String EX = "EX";
// 锁成功标志
private static final String LOCK_SUCCESS = "OK";
// 解锁成功标志
private static final long DEL_SUCCESS = 1L;
// 默认过期时间
private static final int DEFAULT_EXPIRE_TIME = 180;
// 默认重试次数
private static final int DEFAULT_RETRY_TIME = 3;
// 默认重试暂停时间
private static final int DEFAULT_INTERNAL_TIME = 100;
// 解锁时的 lua语言Script
private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
// 过期时间(秒)
private int expireSeconds;
// 锁重试次数
private int retryTimes;
// 锁重试间隔
private int internalTime;
// 加锁者标志, 解锁时只有与加锁时的值一致才允许解锁
private final String lockId;
@Autowired
private static RedisTemplate<String, Object> redisTemplate;
public RedisDistributedLock() {
this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
}
public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate) {
this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
}
public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
this.redisTemplate = redisTemplate;
this.expireSeconds = expireSeconds;
this.retryTimes = retryTimes;
this.internalTime = internalTime;
this.lockId = UUID.randomUUID().toString();
}
/**
* 获取redis 锁
* @Title: lock
* @Description: TODO(这里用一句话描述这个方法的作用)
* @author Administrator
* @date 2018-11-22 下午1:55:57
* @param key
* @return
*/
public boolean lock(String key, final String lockId) {
if (StringUtils.isBlank(key)) {
return false;
}
final String fullKey = PREFIX + key;
//logger.debug("尝试获取锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
System.out.println("尝试获取锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
for (int i = 0; i < retryTimes; i++) {
//logger.debug("第 {} 次尝试获取锁", i + 1);
System.out.println("第 {"+(i + 1)+"} 次尝试获取锁");
String status = redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
try {
Jedis jedis = (Jedis) connection.getNativeConnection();
return jedis.set(fullKey, lockId, NX, EX, expireSeconds);
} catch (Exception e) {
return "ERROR";
}
}
});
if (LOCK_SUCCESS.equalsIgnoreCase(status)) {
//logger.debug("获取锁成功, 锁Key:{}, 锁标识:{}", fullKey, lockId);
System.out.println("获取锁成功, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
return true;
}
try {
Thread.sleep(internalTime);
} catch (InterruptedException e) {
break;
}
}
//logger.debug("尝试获取锁 {} 次后失败, 锁Key:{}", retryTimes, fullKey);
System.out.println("尝试获取锁 {"+retryTimes+"} 次后失败, 锁Key:{"+fullKey+"}" );
return false;
}
/**
* 释放redis锁
* @Title: unlock
* @Description: TODO(这里用一句话描述这个方法的作用)
* @author Administrator
* @date 2018-11-22 下午1:55:34
* @param key
* @return
*/
public boolean unlock(String key, final String lockId) {
if (StringUtils.isBlank(key)) {
return false;
}
final String fullKey = PREFIX + key;
//logger.debug("尝试解锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
System.out.println("尝试解锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
Object value = redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
try {
Jedis jedis = (Jedis) connection.getNativeConnection();
return jedis.eval(DEL_SCRIPT, Collections.singletonList(fullKey), Collections.singletonList(lockId));
} catch (Exception e) {
return -1L;
}
}
});
boolean isUnlock = value!=null && value.equals(DEL_SUCCESS);
if (isUnlock) {
//logger.debug("解锁成功, 锁Key:{}", fullKey);
System.out.println("解锁成功, 锁Key:{"+fullKey+"}");
}
return isUnlock;
}
/**
*
* @Title: get
* @Description: TODO(这里用一句话描述这个方法的作用)
* @author Administrator
* @date 2018-11-22 下午2:38:28
* @param key
* @return
*/
public String get( String key) {
String obj = null;
final String fullKey = PREFIX + key;
try {
obj = redisTemplate.execute(new RedisCallback<String>() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
Jedis jedis = (Jedis) connection.getNativeConnection();
return jedis.get(fullKey);
}
});
String result = obj;
return result;
} catch (Exception e) {
logger.error("get redis occured an exception", e);
}
return "";
}
public String getLockId() {
return lockId;
}
public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate) {
return new RedisDistributedLock(redisTemplate);
}
public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) {
return new RedisDistributedLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
}
public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
return new RedisDistributedLock(redisTemplate, expireSeconds, retryTimes, internalTime);
}
}