package com.mobile.web.portal.util.lock; import redis.clients.jedis.Jedis; public class JedisLock { private int timeoutMsecs; private String lockKey; private static long expireMsecs = 1000 * 60 * 1; // min 鎖持有超時 public JedisLock(String lockKey, Integer timeoutMsecs) { this.timeoutMsecs = timeoutMsecs; this.lockKey = lockKey; } public synchronized boolean acquire(Jedis jedis) throws InterruptedException { int timeout = timeoutMsecs; while (timeout >= 0) { long expires = System.currentTimeMillis() + expireMsecs + 1; String expiresStr = String.valueOf(expires); // 鎖到期時間 if (jedis.setnx(lockKey, expiresStr) == 1) { return true; } String currentValueStr = jedis.get(lockKey); // redis裏的時間 // 表示已經鎖失效,要從新設置鎖 if (currentValueStr != null && Long.parseLong(currentValueStr) < System .currentTimeMillis()) { // 判斷是否爲空,不爲空的狀況下,若是被其餘線程設置了值,則第二個條件判斷是過不去的 // lock is expired String oldValueStr = jedis.getSet(lockKey, expiresStr); // 獲取上一個鎖到期時間,並設置如今的鎖到期時間,; // 只有一個線程才能獲取上一個線上的設置時間,由於jedis.getSet是同步的 if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { // 如過這個時候,多個線程剛好都到了這裏,可是隻有一個線程的設置值和當前值相同,他纔有權利獲取鎖 return true; } } timeout -= 100; Thread.sleep(100); } return false; } public void unLock(Jedis jedis) { jedis.del(lockKey); } }
package com.mobile.web.portal.util.lock; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisShardInfo; import com.zhicall.care.system.basic.BeanFactory; public class RequestValidate { private RedisTemplate<Object, Object> redisTemplate = (RedisTemplate<Object, Object>) BeanFactory.getInstance().getBean("redisTemplate"); private static final String ERR_MSG = "www!"; private static final int TIMEOUTMSECS = 300; /** * 使用redis 分佈式鎖對請求驗證 * * @return */ public String validate(long a, String b){ String lockKey = a+":"+b+"_lock"; int timeoutMsecs = TIMEOUTMSECS; JedisLock lock = new JedisLock(lockKey, timeoutMsecs); Jedis jedis = getJedis(); try { boolean flag = lock.acquire(jedis); if(!flag){ return ERR_MSG; } } catch (InterruptedException e) { return ERR_MSG; }finally { jedis.disconnect(); } return null; } private Jedis getJedis(){ JedisConnectionFactory factory = (JedisConnectionFactory)redisTemplate.getConnectionFactory(); JedisShardInfo shardInfo = factory.getShardInfo(); Jedis jedis = shardInfo.createResource(); return jedis; } public void unLock(long a, String b){ String lockKey = a+":"+b+"_lock"; int timeoutMsecs = TIMEOUTMSECS; JedisLock lock = new JedisLock(lockKey, timeoutMsecs); Jedis jedis = getJedis(); try { lock.unLock(jedis); }finally { jedis.disconnect(); } } }
-------------------------------------------------------------------------------------------------------------------------java
更新web
新的redis分佈式鎖,基於redis2.6版本以上redis
package com.mobile.web.util.redis; import java.util.Collections; import redis.clients.jedis.Jedis; /** * 新的Redis分佈式鎖實現 * 基於jedis版本2.8.0以上 * @author lsnBin * @date 2018/01/17 */ public class JedisTool { private static final String LOCK_SUCCESS = "OK"; /** 即當key不存在時,咱們進行set操做 */ private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; private static final Long RELEASE_SUCCESS = 1L; /** * 嘗試獲取分佈式鎖 * @param jedis Redis客戶端 * @param lockKey 鎖 * @param requestId 請求標識 * @param expireTime 超期時間 * @return 是否獲取成功 */ public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; } /** * 釋放分佈式鎖 * @param jedis Redis客戶端 * @param lockKey 鎖 * @param requestId 請求標識 * @return 是否釋放成功 */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { //Lua代碼傳到jedis.eval()方法裏,並使參數KEYS[1]賦值爲lockKey,ARGV[1]賦值爲requestId。eval()方法是將Lua代碼交給Redis服務端執行 //首先獲取鎖對應的value值,檢查是否與requestId相等,若是相等則刪除鎖(解鎖)。 //eval命令執行Lua代碼的時候,Lua代碼將被當成一個命令去執行,而且直到eval命令執行完成,Redis纔會執行其餘命令。 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; } }
替換緣由spring
一、釋放鎖存在:直接使用redis.del()方法刪除鎖,不限判斷擁有者而直接解鎖的方式,會致使任何客戶端均可以隨時解鎖,即時鎖不是它的。
二、獲取鎖存在:
2.1:因爲是客戶端本身生成過時時間,因此須要強制要求分佈式下每一個客戶端的時間必須同步.
2.2:當鎖過時的時候,若是多個客戶端同時執行jedis.getSet()方法,那麼雖然最終只有一個客戶端能夠加鎖,可是這個客戶端的鎖的過時時間可能被其餘客戶端覆蓋。
2.3:鎖不具有擁有者標識,即任何客戶端均可以解鎖。分佈式