1. 程式人生 > >redis 分散式鎖(單機完美版本)

redis 分散式鎖(單機完美版本)

眾所周知,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());
    }