redis 分散式鎖(單機完美版本)
阿新 • • 發佈:2019-01-09
眾所周知,redis可以實現分散式鎖,如果考慮故障轉移,需要用redlock演算法的支援。如果不考慮,常用的簡單實現如下所示:
/** * 如果鎖可用,則獲取鎖,並立即返回value值。如果鎖不可用,則此方法將立即返回null。 * 不使用固定的字串作為鍵的值,而是設定一個不可猜測(non-guessable)的長隨機字串,作為口令串(token) * @param key redis的key值 * @param keyExpireSecond redis key的有效期 * @return 如果獲取了鎖,則返回 value 否則返回 null。 */ public String tryLock(String key, int keyExpireSecond) { if (Strings.isNullOrEmpty(key) || keyExpireSecond < 0) { throw new IllegalArgumentException("param is illegal"); } String value=fetchLockValue(); if ("ok".equalsIgnoreCase(jedisCluster.set(key, value,"nx","ex",keyExpireSecond ))){ return value; } return null; } /** * 阻塞直到獲取鎖為止 * 不使用固定的字串作為鍵的值,而是設定一個不可猜測(non-guessable)的長隨機字串,作為口令串(token) * @param key * @param keyExpireSecond * @return */ public String lock(String key, int keyExpireSecond){ String value=fetchLockValue(); do { if ("ok".equalsIgnoreCase(jedisCluster.set(key, value, "NX", "EX", keyExpireSecond))) { logger.info("Redis Lock key :{} ,value{}",key,value); return value; } logger.info("Redis lock failure,waiting try next"); try { Thread.sleep(1000); } catch (InterruptedException e) { logger.error("The lock operation is interrupted",e); } } while (true); } /** * 解鎖操作,只在客戶端傳入的值和鍵的口令串相匹配時,才對鍵進行刪除 * 第一個引數命令,第二個引數個數,第三個開始是key,剩下的ARGV * > eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second" * @param key * @param value * @return */ public boolean unLock(String key, String value) { Long RELEASE_SUCCESS = 1L; try { String command = "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; if (RELEASE_SUCCESS.equals(jedisCluster.eval(command, Collections.singletonList(key), Collections.singletonList(value)))) { return true; } } catch (Exception e) { logger.error("Failed to unlock {}",key); } return false; } /** * 生成加鎖的唯一字串 * * @return 唯一字串 */ private String fetchLockValue() { return UUID.randomUUID().toString() + "_" + String.valueOf(System.currentTimeMillis()); }