分散ロックRedis実装

3671 ワード

前編では,分散ロックのデータベース実装について述べたが,分散ロックのredis実装について引き続き述べる.では、redisによって分散ロックを実現するにはどうすればいいのでしょうか.一般的に最も考えやすいコマンドはsetNxですが、redis setNxコマンドを使用する場合、どのような点に注目する必要がありますか?
  • redis分散ロック共通コマンド、SETNX(key,val)キーが存在しない場合のみ、設定に成功し、「1」を返し、否定者は何もせず、「0」を返します.このコマンドの特性を利用してロック操作を行うことができます.同時に2つのスレッドがリソースを競合する場合、そのうちの1つのスレッドが先にリソースを取得し、setnxコマンドを呼び出すと、設定に成功し、「1」に戻り、ロックに成功したことを示します.別のスレッドもsetnxコマンドを呼び出し、keyが設定されていることを示し、ロックに失敗し、再待機する必要があることを示す「0」を返します.
  • expire key timeoutロック中に異常が発生することを防止するために、ロックはずっと解放されず、ロックをかけるkeyにタイムアウト時間、タイムアウト時間の設定を設定する必要があります.すべての分散ロックの実現に考慮する必要があります.
  • delete keyあるkeyを削除し、対応するロックを解放します.

  • 次に、簡単なソース実装を示します.
    /**
     *            
     */
    public class DistributedLock {
    
        private final JedisPool jedisPool;
    
        public DistributedLock(JedisPool jedisPool) {
            this.jedisPool = jedisPool;
        }
    
        /**
         *   
         * @param lockName         key
         * @param acquireTimeout       
         * @param timeout              
         * @return    
         */
        public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
            Jedis conn = null;
            String retIdentifier = null;
            try {
                //     
                conn = jedisPool.getResource();
                //       value
                String identifier = UUID.randomUUID().toString();
                //   , key 
                String lockKey = "lock:" + lockName;
                //     ,              
                int lockExpire = (int) (timeout / 1000);
    
                //         ,            
                long end = System.currentTimeMillis() + acquireTimeout;
                while (System.currentTimeMillis() < end) {
                    if (conn.setnx(lockKey, identifier) == 1) {
                        conn.expire(lockKey, lockExpire);
                        //   value ,         
                        retIdentifier = identifier;
                        return retIdentifier;
                    }
                    //   -1  key        , key        
                    if (conn.ttl(lockKey) == -1) {
                        conn.expire(lockKey, lockExpire);
                    }
    
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.close();
                }
            }
            return retIdentifier;
        }
    
        /**
         *    
         * @param lockName     key
         * @param identifier       
         * @return
         */
        public boolean releaseLock(String lockName, String identifier) {
            Jedis conn = null;
            String lockKey = "lock:" + lockName;
            boolean retFlag = false;
            try {
                conn = jedisPool.getResource();
                while (true) {
                    //   lock,      
                    conn.watch(lockKey);
                    //        value        ,    ,   ,   
                    if (identifier.equals(conn.get(lockKey))) {
                        Transaction transaction = conn.multi();
                        transaction.del(lockKey);
                        List results = transaction.exec();
                        if (results == null) {
                            continue;
                        }
                        retFlag = true;
                    }
                    conn.unwatch();
                    break;
                }
            } catch (JedisException e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.close();
                }
            }
            return retFlag;
        }
    }