ホームページ  >  記事  >  Java  >  Redis を使用して分散ロックを実装する方法の詳細な説明 (コード例)

Redis を使用して分散ロックを実装する方法の詳細な説明 (コード例)

不言
不言転載
2018-10-20 17:00:283449ブラウズ

この記事では、Redis を使用して分散ロックを実装する方法について詳しく説明します (コード例)。必要な方は参考にしてください。

分散ロックの実装に Redis を選択する理由

Redis は高いパフォーマンスを備えています

Redis コマンドはこれをより適切にサポートしており、実装がより便利です

#コマンドの使い方の概要

SETNXSETNX key val

キーが存在しない場合に限り、キー val で文字列を設定し、 1 を返します。キーが存在する場合は何もせず、0 を返します。

expireexpire key timeout

デッドロックを回避するために、この時間が経過するとロックが自動的に解放されます。

deletedelete key

Delete key
Redis を使用して分散ロックを実装する場合、主に次の 3 つのコマンドが使用されます。

実装

は、jedis を使用して Redis に接続します。

実装アイデア

ロックを取得するときは、setnx を使用してロックし、expired コマンドを使用してロックにタイムアウト期間を追加します。ロックは自動的に解放されます。この値はランダムに生成された UUID であり、ロックがいつ解放されるかを決定するために使用されます。

ロックの取得時には、取得のタイムアウト時間も設定されており、この時間を超えるとロックの取得が放棄されます。

ロックを解除する場合は、UUIDを使用してロックであるかどうかを判断し、ロックである場合はdeleteを実行してロックを解除します。

分散ロックのコア コードは次のとおりです。

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;
 
import java.util.List;
import java.util.UUID;
 
public class DistributedLock {
    private final JedisPool jedisPool;
 
    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }
 
    /**
     * 加锁
     * @param locaName  锁的key
     * @param acquireTimeout  获取超时时间
     * @param timeout   锁的超时时间
     * @return 锁标识
     */
    public String lockWithTimeout(String locaName,
                                  long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 获取连接
            conn = jedisPool.getResource();
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();
            // 锁名,即key值
            String lockKey = "lock:" + locaName;
            // 超时时间,上锁后超过此时间则自动释放锁
            int lockExpire = (int)(timeout / 1000);
 
            // 获取锁的超时时间,超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis()  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;
    }
}
Test

以下は、実装されたばかりの分散ロックをテストする簡単な例です。

この例では、製品のフラッシュセールをシミュレートするために 50 スレッドが使用されており、結果の順序から、ロック状態にあるかどうかがわかります。 。

フラッシュ セール サービスをシミュレートし、その中で jedis スレッド プールを構成し、それを使用するための初期化中に分散ロックに渡します。

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class Service {
    private static JedisPool pool = null;
 
    static {
        JedisPoolConfig config = new JedisPoolConfig();
        // 设置最大连接数
        config.setMaxTotal(200);
        // 设置最大空闲数
        config.setMaxIdle(8);
        // 设置最大等待时间
        config.setMaxWaitMillis(1000 * 100);
        // 在borrow一个jedis实例时,是否需要验证,若为true,则所有jedis实例均是可用的
        config.setTestOnBorrow(true);
        pool = new JedisPool(config, "127.0.0.1", 6379, 3000);
    }
 
    DistributedLock lock = new DistributedLock(pool);
 
    int n = 500;
 
    public void seckill() {
        // 返回锁的value值,供释放锁时候进行判断
        String indentifier = lock.lockWithTimeout("resource", 5000, 1000);
        System.out.println(Thread.currentThread().getName() + "获得了锁");
        System.out.println(--n);
        lock.releaseLock("resource", indentifier);
    }
}
Flash Killing Service のスレッドをシミュレートする

public class ThreadA extends Thread {
    private Service service;
 
    public ThreadA(Service service) {
        this.service = service;
    }
 
    @Override
    public void run() {
        service.seckill();
    }
}
 
public class Test {
    public static void main(String[] args) {
        Service service = new Service();
        for (int i = 0; i 結果は次のとおりです。結果は順番に表示されます。 <p></p><p style="text-align: center;"><span class="img-wrap"><img src="https://img.php.cn//upload/image/211/984/157/1540025904941327.png" title="1540025904941327.png" alt="Redis を使用して分散ロックを実装する方法の詳細な説明 (コード例)"></span></p>ロックを使用している部分をコメントアウトすると<p></p><pre class="brush:php;toolbar:false">public void seckill() {
    // 返回锁的value值,供释放锁时候进行判断
    //String indentifier = lock.lockWithTimeout("resource", 5000, 1000);
    System.out.println(Thread.currentThread().getName() + "获得了锁");
    System.out.println(--n);
    //lock.releaseLock("resource", indentifier);
}
結果から、一部が非同期で実行されていることがわかります。

Redis を使用して分散ロックを実装する方法の詳細な説明 (コード例)

分散環境では、特定のリソースをスナップするなど、リソースをロックすることが重要になる場合があります。この場合、分散ロックを使用できます。リソースを適切に制御できます。

もちろん、特定の使用法では、タイムアウト時間の選択やロック取得時間の選択など、実装される分散ロックの量に大きな影響を与える多くの要素を考慮する必要があります。上記は一種の単純な実装であり、主にアイデアです。

[実際には、zookeeper を使用する信頼性は、redis によって実装される分散ロックよりも優れていますが、比較すると、redis のパフォーマンスが優れています。 ]

以上がRedis を使用して分散ロックを実装する方法の詳細な説明 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。