DCS実践乾物:Redisを使用して分散ロックを実現

7972 ワード

テキストリンク:https://segmentfault.com/a/1190000015413658
シーンは多くのインターネットシーン(商品の秒殺、フォーラムのレスビルなど)を紹介し、ある資源に対して順次アクセス制御を行うためにロックをかける必要がある.アプリケーション・サービス・クラスタの導入は、分散アプリケーションのロックに関連します.現在の分散ロックには、(ディスク)データベース、キャッシュ・データベース、Zookeeperの3つの方法があります.次に、ロックの実践過程を見てみましょう.
ロックはpackage dcsDemo 01を実現する.import java.util.UUID;import redis.clients.jedis.Jedis;public class DistributedLock {
private final String host = "192.168.0.220";
private final int port = 6379;
private static final String SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String EXPIRE_TIME = "PX";
public  DistributedLock(){}
/*
 * @param lockName        
 * @param timeout               
 * @param lockTimeout         
 * @return                  
 */
public String getLockWithTimeout(String lockName, long timeout, long lockTimeout) {
    String ret = null;
    Jedis jedisClient = new Jedis(host, port);
    try {
        String authMsg = jedisClient.auth("Demo@123");
        if (!SUCCESS.equals(authMsg)) {
            System.out.println("AUTH FAILED: " + authMsg);
        }

        String identifier = UUID.randomUUID().toString();
        String lockKey = "DLock:" + lockName;
        long end = System.currentTimeMillis() + timeout;

        while(System.currentTimeMillis() < end) {
            String result = jedisClient.set(lockKey, identifier, SET_IF_NOT_EXIST, EXPIRE_TIME, lockTimeout);
            if(SUCCESS.equals(result)) {
                ret = identifier;
                break;
            }

            try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }finally {
        jedisClient.quit();
        jedisClient.close();
    }

    return ret;
}

/*
 * @param lockName          
 * @param identifier        
 */
public void releaseLock(String lockName, String identifier) {
    Jedis jedisClient = new Jedis(host, port);

    try {
        String authMsg = jedisClient.auth("Demo@123");
        if (!SUCCESS.equals(authMsg)) {
            System.out.println("AUTH FAILED: " + authMsg);
        }

        String lockKey = "DLock:" + lockName;
        if(identifier.equals(jedisClient.get(lockKey))) {
            jedisClient.del(lockKey);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }finally {
        jedisClient.quit();
        jedisClient.close();
    }
}

}
テストコードは20のスレッドが10台のmate 10携帯電話を買い占めると仮定する:package dcsDemo 01;import java.util.UUID;
public class CaseTest {
public static void main(String[] args) {
    ServiceOrder service = new ServiceOrder();
    for (int i = 0; i < 20; i++) {
        ThreadBuy client = new ThreadBuy(service);
        client.start();
    }
}

}
class ServiceOrder {
private final int MAX = 10;

DistributedLock DLock = new DistributedLock();

int n = 10;

public void handleOder() {
    String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();
    String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);
    System.out.println("     :" + userName + "     ");
    if(n > 0) {
        int num = MAX - n + 1;
        System.out.println("  :"+ userName + "   " + num + " ,  " + (--n) + " ");
    }else {
        System.out.println("  :"+ userName + "    ,   !");
    }
    DLock.releaseLock("Huawei Mate 10", identifier);
}

}
class ThreadBuy extends Thread {
private ServiceOrder service;

public ThreadBuy(ServiceOrder service) {
    this.service = service;
}

@Override
public void run() {
    service.handleOder();
}

}
実行結果は実際のキャッシュインスタンス接続アドレス、ポートと接続パスワードを構成し、コードを実行し、以下の結果を得た:eee 56 fb 7 Thread-16のために注文を処理しているユーザー:eee 56 fb 7 Thread-16は第1台を購入し、残りの9台はユーザー:d 6521816 Thread-2で注文を処理しているユーザー:d 6521816 Thread-2は第2台を購入し、残りの8台はユーザー:d 7 b 3 b 983 Thread-19の受注処理ユーザー:d 7 b 3 b 983 Thread-19の3台目の購入、残りの7台はユーザー:36 a 6 b 97 aThread-15の受注処理ユーザー:36 a 6 b 97 aThread-15の4台目の購入、残りの6台はユーザー:9 a 973456 Thread-1の受注処理ユーザー:9 a 973456 Thread-1の5台目の購入、残り5台はユーザー:03 f 1 de 9 aThread-14受注処理ユーザー:03 f 1 de 9 aThread-14 6台目、残り4台はユーザー:2 c 315 ee 6 Thread-11受注処理ユーザー:2 c 315 ee 6 Thread-11 7台目、残り3台はユーザー:2 b 03 b 7 c 0 Thread-12受注処理ユーザー:2 b 03 b 7 c 0 Thread-12 8台目、残りの2台はユーザー:75 f 25749 Thread-0処理オーダーユーザー:75 f 25749 Thread-0購入9台目、残りの1台はユーザー:26 c 71 db 5 Thread-18処理オーダーユーザー:26 c 71 db 5 Thread-18購入10台目、残りの0台はユーザー:c 32654 dbThread-17処理オーダーユーザー:c 32654 dbThread-17購入できず、売り切れ!ユーザー:df 94370 aThread-7の受注処理ユーザー:df 94370 aThread-7は購入できません.売り切れました.ユーザー:0 af 94 cddThread-5の受注処理ユーザー:0 af 94 cddThread-5は購入できません.売り切れました.ユーザー:e 52428 a 4 Thread-13の受注処理ユーザー:e 52428 a 4 Thread-13は購入できません.売り切れました.ユーザー:46 f 91208 Thread-10の受注処理ユーザー:46 f 91208 Thread-10は購入できません.売り切れました.ユーザー:e 0 ca 87 bbThread-9の受注処理ユーザー:e 0 ca 87 bbThread-9は購入できません.売り切れました.ユーザー:f 385 af 9 aThread-8の受注処理ユーザー:f 385 af 9 aThread-8は購入できません.売り切れました.ユーザー:46 c 5 f 498 Thread-6の受注処理ユーザー:46 c 5 f 498 Thread-6は購入できません.売り切れました.ユーザー:935 e 0 f 50 Thread-3の受注処理ユーザー:935 e 0 f 50 Thread-3は購入できません.売り切れました.ユーザー:d 3 eaae 29 Thread-4の受注処理ユーザー:d 3 eaae 29 Thread-4は購入できません.売り切れました.
ロックなしシーンロックコードを注釈してロックなしになった場合、買い占めは無秩序です.//テストクラスの注釈2行のロック用コード:public void handleOder(){
String userName = UUID.randomUUID().toString().substring(0,8) + Thread.currentThread().getName();
//    
//String identifier = DLock.getLockWithTimeout("Huawei Mate 10", 10000, 2000);
System.out.println("     :" + userName + "     ");
if(n > 0) {
    int num = MAX - n + 1;
    System.out.println("  :"+ userName + "   " + num + " ,  " + (--n) + " ");
}else {
    System.out.println("  :"+ userName + "    ,   !");
}
//    
//DLock.releaseLock("Huawei Mate 10", identifier);

}
注記ロックコードを追加した後の実行結果は、処理プロセスが無秩序であることがわかる:ユーザー:e 04934 ddThread-5処理オーダーがユーザー:a 4554180 Thread-0処理オーダーユーザー:a 4554180 Thread-0購入2台目、残りの8台がユーザー:b 58 eb 811 Thread-10処理オーダーユーザー:b 58 eb 811 Thread-10購入3台目、残りの7台はユーザー:e 8391 c 0 eThread-19受注処理ユーザー:21 fd 133 aThread-13受注処理ユーザー:1 dd 04 ff 4 Thread-6受注処理ユーザー:1 dd 04 ff 4 Thread-6 6 6購買第6台、残りの4台はユーザー:e 5977112 Thread-3受注処理ユーザー:4 d 7 a 2 bThread-4受注処理ユーザー:e 5977112 Thread-3購買第7台、残りの3台はユーザー:18967410 Thread-15のために注文を処理しているユーザー:18967410 Thread-15は9台目を購入し、残りの1台はユーザー:e 4 f 51568 Thread-14のために注文を処理しているユーザー:21 fd 133 aThread-13は5台目を購入し、残りの5台のユーザー:e 8391 c 0 eThread-19は4台目を購入し、残りの6台はユーザー:d 895 d 3 f 1 Thread-12のために注文を処理しているユーザー:d 895 d 3 f 1 Thread-12は購入できません.売り切れました!ユーザー:7 b 8 d 2526 Thread-11の受注処理ユーザー:7 b 8 d 2526 Thread-11は購入できません.売り切れました.ユーザー:d 7 ca 1779 Thread-8の受注処理ユーザー:d 7 ca 1779 Thread-8は購入できません.売り切れました.ユーザー:74 fca 0 ecThread-1の受注処理ユーザー:74 fca 0 ecThread-1は購入できません.売り切れました.ユーザー:e 04934 ddThread-5は1台目を購入し、残りの9台のユーザー:e 4 f 51568 Thread-14は10台目を購入し、残りの0台はユーザー:aae 76 a 83 Thread-7のために注文を処理しているユーザー:aae 76 a 83 Thread-7は購入できず、売り切れた!ユーザー:c 638 d 2 cfThread-2の受注処理ユーザー:c 638 d 2 cfThread-2は購入できません.売り切れました.ユーザー:2 de 29 a 4 eThread-17の受注処理ユーザー:2 de 29 a 4 eThread-17は購入できません.売り切れました.ユーザー:40 a 46 ba 0 Thread-18の受注処理ユーザー:40 a 46 ba 0 Thread-18は購入できません.売り切れました.ユーザー:211 fd 9 c 7 Thread-9の受注処理ユーザー:211 fd 9 c 7 Thread-9は購入できません.売り切れました.ユーザー:911 b 83 fcThread-16の受注処理ユーザー:911 b 83 fcThread-16は購入できません.売り切れました.ユーザー:4 d 7 a 8 a 2 b Thread-4購入8台目、残り2台
総じて、DCSサービスにおけるRedisタイプのキャッシュインスタンスを用いて分散ロックを実現するには、1、ロック操作が簡単で、SET、GET、DELなどのいくつかの簡単なコマンドを使用してロックの取得と解放を実現できるといういくつかの利点がある.2、性能は優越で、キャッシュデータの読み書きはディスクデータベースとZookeeperより優れている.3、信頼性が高く、DCSにはプライマリとクラスタのインスタンスタイプがあり、単一の障害を避ける.
以上のコード実装は、DCSサービスを使用してロックアクセスを行う利便性のみを示しており、具体的な技術実装では、デッドロック、ロックのチェックなどを考慮する必要があります.分散キャッシュサービスDCSをクリックしてより多くのことを理解してください.