在Java中使用redis分佈式鎖,業務邏輯跨多個方法,怎麼釋放分佈式鎖

boolean before(){
	lock(){//併發下單加分佈式鎖
		//判斷是否有下單資格 是否超過每日下單數
		if(true){
			return true;
		}else{
			return false;
		}
	}
};
validate(){};//參數校驗之類的
after(){
	redis.inrc();//統計每日下單數
}
//業務主線
if(before()){
	validate();
	after();
}
/**
     * 獲取分佈式鎖
     * @param jedis
     * @param lockKey 分佈式鎖Key
     * @param expireTime 分佈式鎖過期時間
     * @param timeoutTime 獲取分佈式鎖最大時間
     * @return 分佈式鎖持有者Key,未獲取到鎖爲null
     */
    public static String getDistributedLock(Jedis jedis, String lockKey, int expireTime,int timeoutTime) {
        boolean result = false;
        // 隨機生成一個value
        String requestId = UUID.randomUUID().toString();
        // 獲取鎖的超時時間,超過這個時間則放棄獲取鎖
        long end = System.currentTimeMillis() + timeoutTime;
        while (!result && System.currentTimeMillis() < end) {
            result = tryGetDistributedLock(jedis, lockKey, requestId, expireTime);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        return result ? requestId : null;
    }
/**
     * 嘗試獲取分佈式鎖
     * @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, "NX", "PX", 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) {
        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));
        returnResource(jedis);
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

/**
     * 釋放jedis資源
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null) {
        	jedis.close();
        }
    }

對於分佈式鎖,我們應該秉持 誰用誰釋放的原則,不可以等到鎖過期自動釋放。
以上僞代碼在before方法添加分佈式鎖後,什麼時候釋放呢?
按照正常的業務邏輯,應該是在before加鎖,等到after執行完後釋放,但是此時可能會釋放掉其他的鎖,造成業務混亂。如果等鎖自動過期,併發時必定會消耗大量的系統資源,造成不可預知的問題。
如果在before裏面在方法執行完後釋放鎖,在前一個請求執行validate() after()邏輯時,下一個訂單可能早就進before來了,就失去了限量的目的。
最後怎麼解決的呢?

  1. getDistributedLock(" “,” ",2*1000,60*1000)正常的業務邏輯一般2s內肯定可以執行完。
  2. 在before try catch finally 調用releaseDistributedLock釋放鎖時 使用線程延遲1s釋放,給業務足夠執行的時間。
String requestId = RedisUtil.getDistributedLock(jedis, lockKey, 2*1000, 60*1000);
if (null != requestId) {
try {
	...		
} catch (Exception e) {
	log.error("系統下單限量管控error:"+e);
} finally {
	new Thread(new Runnable() {
		@Override
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				releaseDistributedLock(jedis, lockKey, requestId);
			}
		}
	}).start();
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章