- 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 发布