Redis实现分布式锁通用工具

  1. import java.net.InetAddress;  
  2. import java.net.UnknownHostException;  
  3.   
  4. import javax.annotation.Resource;  
  5.   
  6. import org.apache.commons.lang3.StringUtils;  
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9. import org.springframework.stereotype.Service;  
  10.   
  11. import com.qunar.flight.qmonitor.QMonitor;  
  12. import com.qunar.redis.storage.Sedis;  
  13.   
  14. @Service  
  15. public class RedisDistributedLockService {  
  16.     private final Logger logger = LoggerFactory.getLogger(RedisDistributedLockService.class);  
  17.   
  18.     // redis的lock的key的最长有效时间  
  19.     public static final long LOCK_VALIDITY_TIME = 60 * 1000l;  
  20.     // 获取redis集群锁的重试时间  
  21.     public static final int ETRY_REDIS_LOCK_MILLIS = 10;  
  22.   
  23.     public static final long LOCK_VALIDITY_ONE_MINUTES = 30 * 1000L;  
  24.   
  25.     public static final int RETRY_MAX_TIMES = 10;// 获取锁 做多重试  
  26.     @Resource  
  27.     private Sedis sedis;  
  28.   
  29.     /** 
  30.      * 获取锁,一直等待到取到锁后返回 
  31.      *  
  32.      * @param key 
  33.      * @return 
  34.      */  
  35.     public String getUntilHaveLock(String key) {  
  36.         String randomLockValue = computeLockRandomValue();  
  37.         int times = 0;  
  38.         Long nowT = System.currentTimeMillis();  
  39.         Long endT = nowT + LOCK_VALIDITY_ONE_MINUTES;// 超过一分钟 也放弃  
  40.         while (true) {  
  41.             times++;  
  42.             String ret = getLock(key, randomLockValue, LOCK_VALIDITY_TIME);  
  43.             if (StringUtils.isBlank(ret)) {  
  44.                 try {  
  45.                     logger.info("waiting redis lock. the key is {}", key);  
  46.                     Thread.sleep(ETRY_REDIS_LOCK_MILLIS);  
  47.                 } catch (InterruptedException e) {  
  48.                     logger.error("get lock thread sleep error. {}", e);  
  49.                 }  
  50.             } else {  
  51.                 QMonitor.recordOne("get_redis_lock");  
  52.                 return ret;  
  53.             }  
  54.               
  55.             if (times >= RETRY_MAX_TIMES) { // 超过放弃  
  56.                 logger.error("获取锁超过重试次数 放弃获取");  
  57.                 return null;  
  58.             }  
  59.   
  60.             if (System.currentTimeMillis() > endT) {  
  61.                 logger.error("获取锁时间超过截止时间  放弃获取");  
  62.                 return null;  
  63.             }  
  64.         }  
  65.     }  
  66.   
  67.     /** 
  68.      * 获取锁 
  69.      *  
  70.      * @param key 
  71.      * @param expireMillis 锁过期时间(毫秒) 
  72.      * @return 
  73.      */  
  74.     public String getLock(String key, long expireMillis) {  
  75.         return getLock(key, computeLockRandomValue(), expireMillis);  
  76.     }  
  77.   
  78.     private String getLock(String key, String randomLockValue, long expireMillis) {  
  79.         logger.info("key:{},randomLockValue:{}", key, randomLockValue);  
  80.         String ret = sedis.set(key, randomLockValue, "NX""PX", expireMillis);  
  81.         if (StringUtils.isNotEmpty(ret) && ret.equalsIgnoreCase("OK")) {  
  82.             QMonitor.recordOne("get_redis_lock");  
  83.             return randomLockValue;  
  84.         }  
  85.         return StringUtils.EMPTY;  
  86.     }  
  87.   
  88.     /** 
  89.      * 由于get和del不在一个事物里面,所以是存在风险的 但是tc的sedis客户端不支持eval这样的功能,太蛋疼了。 
  90.      *  
  91.      * @param key 
  92.      * @param value 
  93.      * @return 
  94.      */  
  95.     public int releaseLock(String key, String value) {  
  96.         String redisValue = sedis.get(key);  
  97.         if (StringUtils.isNotEmpty(redisValue) && redisValue.equalsIgnoreCase(value)) {  
  98.             return sedis.del(key).intValue();  
  99.         } else {  
  100.             QMonitor.recordOne("redis_lock_value_empty");  
  101.             logger.error("release redis lock error. redis lock value empty. key is {}, value is {}", key, value);  
  102.             return 0;  
  103.         }  
  104.     }  
  105.   
  106.     private String computeLockRandomValue() {  
  107.         String hostname = getHostname();  
  108.         if (StringUtils.isNotEmpty(hostname)) {  
  109.             return hostname + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();  
  110.         } else {  
  111.             QMonitor.recordOne("hostname_empty");  
  112.             logger.error("hostname is empty");  
  113.             return "host" + SysConsts.FIELD_SEPARATOR + System.currentTimeMillis();  
  114.         }  
  115.     }  
  116.   
  117.     private String getHostname() {  
  118.         try {  
  119.             return InetAddress.getLocalHost().getHostName();  
  120.         } catch (UnknownHostException e) {  
  121.             logger.error("get local hostname error. the error is {}", e);  
  122.         }  
  123.         return null;  
  124.     }  
  125. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值