Redis分佈式鎖
1.Redis Template實現分佈式鎖
package org.ssgroup.secondkill.utils;
import javax.annotation.Resource;
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.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
/**
* 分佈式鎖的實現
* @author HX-011
*
*/
@Component
public class ReidsDistributionLock {
private final static Logger logger = LoggerFactory.getLogger(ReidsDistributionLock.class);
private String DEFAULT_REDIS_LOCK_KEY = "default_redis_lock";
private long DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 3000;
@Resource(name="stringRedisTemplate")
private RedisTemplate<String, String> stringRedisTemplate;
public synchronized boolean tryLock() {
return tryLock(null,null);
}
public synchronized boolean tryLock(String lockKey) {
return tryLock(lockKey,null);
}
public synchronized boolean tryLock(String lockKey,Long timeOut) {
String redisLockKey = DEFAULT_REDIS_LOCK_KEY;
if(!StringUtils.isEmpty(lockKey)) {
redisLockKey = lockKey + "_lock";
}
long expires = System.currentTimeMillis() + DEFAULT_ACQUIRY_RESOLUTION_MILLIS + 1;
if(null!=timeOut && timeOut > 0) {
expires = System.currentTimeMillis() + timeOut + 1;
}
//獲取鎖
boolean isLock = this.setNX(redisLockKey, String.valueOf(expires));
if(isLock) {
logger.info("獲取到分佈式鎖:{}", redisLockKey);
return true;
}
//判斷是否超時
String expireStr = this.get(redisLockKey);
if(!StringUtils.isEmpty(expireStr) && Long.parseLong(expireStr) < System.currentTimeMillis()) {
//獲取上個鎖時間
long newExpires = System.currentTimeMillis() + DEFAULT_ACQUIRY_RESOLUTION_MILLIS + 1;
if(null!=timeOut && timeOut > 0) {
newExpires = System.currentTimeMillis() + timeOut + 1;
}
String oldExpiresStr = this.getSet(redisLockKey, String.valueOf(newExpires));
//防止誤刪
if(!StringUtils.isEmpty(oldExpiresStr) && oldExpiresStr.equals(expireStr)) {
logger.info("獲取到分佈式鎖:{}", redisLockKey);
return true;
}
}
logger.debug("沒有獲取到分佈式鎖:{}", redisLockKey);
return false;
}
/**
* 多服務器集羣,使用下面的方法,代替System.currentTimeMillis(),獲取redis時間,避免多服務的時間不一致問題!!!
* @return
*/
public long currtTimeForRedis() {
return stringRedisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.time();
}
});
}
private boolean setNX(final String key,final String expire) {
Object isLock = false;
try {
isLock = stringRedisTemplate.execute(new RedisCallback<Object> () {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisSerializer serializer = new StringRedisSerializer();
return connection.setNX(serializer.serialize(key), serializer.serialize(expire));
}
});
}catch (Exception e) {
e.printStackTrace();
logger.error("setNX redis error, key : {} | value : {}", key, expire);
}
return null==isLock?false:(Boolean) isLock;
}
private String get(final String key) {
Object obj = null;
try {
obj = stringRedisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.get(serializer.serialize(key));
return data==null?null:serializer.deserialize(data);
}
});
}catch (Exception e) {
e.printStackTrace();
logger.error("get redis error, key : {} | return value : {}", key, obj);
}
return obj==null?null:obj.toString();
}
private String getSet(final String key,final String value) {
Object obj = null;
try {
obj = stringRedisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.getSet(serializer.serialize(key), serializer.serialize(value));
return data==null?null:serializer.deserialize(data);
}
});
}catch (Exception e) {
e.printStackTrace();
logger.error("getSet redis error, key : {} | new value : {} | return value : {}", key, value, obj);
}
return obj==null?null:obj.toString();
}
public synchronized void unlock() {
stringRedisTemplate.delete(DEFAULT_REDIS_LOCK_KEY);
}
public synchronized void unlock(String lockKey) {
stringRedisTemplate.delete(lockKey + "_lock");
}
}