Redis並發鎖控制


為了防止用戶在頁面上重復點擊或者同時發起多次請求,請求處理需要操作redis緩存,這個時候需要對並發邊界進行並發鎖控制,實現思路:

由於每個頁面發起的請求帶的token具備唯一性,可以將token作為鎖(key),當前時間作為value進行並發鎖控制,分為兩個方法:acquireLock和realeaseLock

/**嘗試獲取鎖並設置有效時間*/
     53
+    public boolean acquireLock(String lock, long expired){
     54
+        boolean isSuccess = false;
     55
+        Jedis jedis = jedisPool.getResource();
     56
+        long value = System.currentTimeMillis() + expired + 1;
     57
+        long acquired = jedis.setnx(lock, String.valueOf(value));
     58
+        if (acquired == 1)
     59
+            isSuccess = true;
     60
+        else {
     61
+            long oldValue = Long.valueOf(jedis.get(lock));
     62
+            //如果其他資源之前獲得鎖已經超時
     63
+            if (oldValue < System.currentTimeMillis()){
     64
+                String getValue = jedis.getSet(lock, String.valueOf(value));
     65
+                if (Long.valueOf(getValue) == oldValue)
     66
+                    isSuccess = true;
     67
+                else
     68
+                    isSuccess = false;
     69
+            }
     70
+            else isSuccess = false;
     71
+        }
     72
+        jedisPool.returnResource(jedis);
     73
+        return isSuccess;
     74
+    }
     75
+
     76
+    /**釋放鎖資源*/
     77
+    public void releaseLock(String lock){
     78
+        Jedis jedis = jedisPool.getResource();
     79
+        long currentTime = System.currentTimeMillis();
     80
+        if (currentTime < Long.valueOf(jedis.get(lock))){
     81
+            jedis.del(lock);
     82
+        }
     83
+        jedisPool.returnResource(jedis);
     84
+    }

經過代碼review,對照開源的jedisLock源碼,發現以上實現邏輯問題在於,對於jedisPool.getResource如果發生異常,沒有對異常進行處理,在外面包裝類加上如下處理:

Object runTask(Callback callback) {
        Jedis jedis = null;
        boolean broken = false;
        try {
            jedis = jedisPool.getResource();
            return callback.onTask(jedis);
        } catch (JedisException e) {
            broken = handleJedisException(e);
        } catch (Exception e) {
            log.error("Redis runTask error: ", e);
        } finally {
            closeResource(jedis, broken);
            jedis = null;
        }
        return null;
    }
private boolean handleJedisException(JedisException jedisException) {
        if (jedisException instanceof JedisConnectionException) {
            log.error("Redis connection lost.", jedisException);
        } else if (jedisException instanceof JedisDataException) {
            if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
                log.error("Redis connection are read-only slave.", jedisException);
            } else {
                // dataException, isBroken=false
                return false;
            }
        } else {
            log.error("Jedis exception happen.", jedisException);
        }
        return true;
    }
private void closeResource(Jedis jedis, boolean conectionBroken) {
        try {
            if (conectionBroken) {
                jedisPool.returnBrokenResource(jedis);
            } else {
                jedisPool.returnResource(jedis);
            }
        } catch (Exception e) {
            log.error("return back jedis failed, will fore close the jedis.", e);
            jedis.close();
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM