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();
        }
    }

 

转载于:https://www.cnblogs.com/harlenzhang/p/5395568.html

你可能感兴趣的:(Redis并发锁控制)