import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.qunar.flight.qmonitor.QMonitor;
import com.qunar.redis.storage.Sedis;
@Service
public class RedisDistributedLockService {
private final Logger logger = LoggerFactory.getLogger(RedisDistributedLockService.class);
// redis的lock的key的最长有效时间
public static final long LOCK_VALIDITY_TIME = 60 * 1000l;
// 获取redis集群锁的重试时间
public static final int ETRY_REDIS_LOCK_MILLIS = 10;
public static final long LOCK_VALIDITY_ONE_MINUTES = 30 * 1000L;
public static final int RETRY_MAX_TIMES = 10;// 获取锁 做多重试
@Resource
private Sedis sedis;
/**
* 获取锁,一直等待到取到锁后返回
*
* @param key
* @return
*/
public String getUntilHaveLock(String key) {
String randomLockValue = computeLockRandomValue();
int times = 0;
Long nowT = System.currentTimeMillis();
Long endT = nowT + LOCK_VALIDITY_ONE_MINUTES;// 超过一分钟 也放弃
while (true) {
times++;
String ret = getLock(key, randomLockValue, LOCK_VALIDITY_TIME);
if (StringUtils.isBlank(ret)) {
try {
logger.info("waiting redis lock. the key is {}", key);
Thread.sleep(ETRY_REDIS_LOCK_MILLIS);
} catch (InterruptedException e) {
logger.error("get lock thread sleep error. {}", e);
}
} else {
QMonitor.recordOne("get_redis_lock");
return ret;
}
if (times >= RETRY_MAX_TIMES) { // 超过放弃
logger.error("获取锁超过重试次数 放弃获取");
return null;
}
if (System.currentTimeMillis() > endT) {
logger.error("获取锁时间超过截止时间 放弃获取");
return null;
}
}
}
/**
* 获取锁
*
* @param key
* @param expireMillis 锁过期时间(毫秒)
* @return
*/
public String getLock(String key, long expireMillis) {
return getLock(key, computeLockRandomValue(), expireMillis);
}
private String getLock(String key, String randomLockValue, long expireMillis) {
logger.info("key:{},randomLockValue:{}", key, randomLockValue);
String ret = sedis.set(key, randomLockValue, "NX", "PX", expireMillis);
if (StringUtils.isNotEmpty(ret) && ret.equalsIgnoreCase("OK")) {
QMonitor.recordOne("get_redis_lock");
return randomLockValue;
}
return StringUtils.EMPTY;
}
/**
* 由于get和del不在一个事物里面,所以是存在风险的 但是tc的sedis客户端不支持eval这样的功能,太蛋疼了。
*
* @param key
* @param value
* @return
*/
public int releaseLock(String key, String value) {
String redisValue = sedis.get(key);
if (StringUtils.isNotEmpty(redisValue) && redisValue.equalsIgnoreCase(value)) {
return sedis.del(key).intValue();
} else {
QMonitor.recordOne("redis_lock_value_empty");
logger.error("release redis lock error. redis lock value empty. key is {}, value is {}", key, value);
return 0;
}
}
private String computeLockRandomValue() {
String hostname = getHostname();
if (StringUtils.isNotEmpty(hostname)) {
return hostname + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();
} else {
QMonitor.recordOne("hostname_empty");
logger.error("hostname is empty");
return "host" + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();
}
}
private String getHostname() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
logger.error("get local hostname error. the error is {}", e);
}
return null;
}
}
Redis实现分布式锁通用工具
最新推荐文章于 2024-08-21 22:53:45 发布