Redisson分散ロック実装解析

13253 ワード

RedissonLockコア分析
サンプル分析
RedissonLockを解析する前に、まずredisのluaスクリプトとluaスクリプトを実行する方法を大まかに理解しなければなりません.RedissonLockのコアコードはluaスクリプトコードであるからです.
luaスクリプトの例:
local times = redis.call('incr',KEYS[1])
if times == 1 then
    redis.call('expire',KEYS[1], ARGV[1])
end
if times > tonumber(ARGV[2]) then
    return 0
end
return 1

実行:*redis-cli–eval ratelimiting.lua rate.limitingl:127.0.0.1,103*分析解釈:*–evalパラメータはredis-cliに後のLuaスクリプトを読み取り実行するように伝え、ratelimiting.luaはスクリプトの位置であり,後にLuaスクリプトに渡されるパラメータが続く.その中で、「前のrate.limiting:127.0.0.1は操作するキーで、スクリプトではKEYS[1]で取得できます」、「後の10と3はパラメータで、スクリプトではARGV[1]とARGV[2]で取得できます.注意:「両方のスペースは省略できません.エラーが発生します.*スクリプトの内容に合わせて、この行のコマンドの役割は、アクセス頻度を10秒ごとに最大3回に制限することです.そのため、端末でこのコマンドを繰り返し実行すると、アクセス頻度が10秒以内に3回未満の場合は1を返し、そうでない場合は0を返します.
RedissonLock分析
RLockはRedissonベースの同期ロック実装である
次の基本的な使用方法を示します.
RLock rLock = redisson.getLock("lock");
rLock.lock(100, TimeUnit.SECONDS);
//Todo: your code!
rLock.unlock();

説明:*RLockの使い方は実はReentrantLockの使い方と似ています*異なる点はRLockにkey IDがあることです*RLockの生存時間を設定することもできます(ロックを自動的に解放する時間は、ReentrantLockとは異なり、ReentrantLockは手動でロックを解放する必要があります)*同時にRLockは手動でロックを解除することもできます*以上は使い方の表面レベルから見るとReentrantLockと差が少ないだけで、本質的には差が大きいのか*同じならなぜRLockを一挙に使うのか...*ここでは、最大の違いはRLockがRedisの特性を利用して分散ロックメカニズムを実現していることです.これはReentrantLockにはない特性です
rLockを取得してからキーコードのロックを開始します.以下はRedissonがlockを実行するプロセスです.
@Override
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
    Long ttl = tryAcquire(leaseTime, unit);//-->    tryLockInnerAsync,          
    // lock acquired
    if (ttl == null) {//  lua              ,         
        return;
    }

    long threadId = Thread.currentThread().getId();
    Future future = subscribe(threadId);
    get(future);

    try {
        while (true) {
            ttl = tryAcquire(leaseTime, unit);
            // lock acquired
            if (ttl == null) {
                break;
            }

            // waiting for message
            if (ttl >= 0) {
                getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
            } else {
                getEntry(threadId).getLatch().acquire();
            }
        }
    } finally {
        unsubscribe(future, threadId);
    }
}

tryLockInnerAsync:
ロック(非同期)*–1つのキー、2つのパラメータ*keys="lock"*args="30000 3 a 4 e 8 d 51-f 191-40 a 4-8220-f 80 d 39759 bc 0:1"*{keys:lock->RLock存在フラグargs:30000->RLock生存時間、単位ms 3 a 4 e 8 d 51-f 191-40 a 4-8220-f 80 d 39759 bc 0:1->RLock同一実行スレッド一意フラグ、主に同一実行スレッド}
if (redis.call('exists', KEYS[1]) == 0) then --  RLock   
  redis.call('hset', KEYS[1], ARGV[2], 1);   --   hset        RLock
  redis.call('pexpire', KEYS[1], ARGV[1]);   --  RLock    ,pexpire expire  ,  pexpire      ms, expire s
  return nil;                                              --  RLock
end;  
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then  --         RLock  ,    ,  RLock    1,  RLock    
  redis.call('hincrby', KEYS[1], ARGV[2], 1);               --RLock   1
  redis.call('pexpire', KEYS[1], ARGV[1]);              --  RLock    
  return nil;                                           --  RLock
end;  
return redis.call('pttl', KEYS[1]);  --    RLock      

unlock:
ロック解除*–2つのキー、3つのパラメータ*keys="lock redisson_lock__channel__{lock}"*args="0 30000 3 a 4 e 8 d 51-f 191-40 a 4-8220-f 80 d 39759 bc 0:1"*{keys:key->Rlockフラグredisson_lock__channel_{lock}->指定されたチャンネルchannel(PUBLISH channel message、その他の情報はpublishコマンドを参照)args:0->30000->RLock生存時間、単位ms 3 a 4 e 8 d 51-f 191-40 a 4-8220-f 80 d 39759 bc 0:1->同一実行スレッドRLockフラグ}
if (redis.call('exists', KEYS[1]) == 0) then --RLock   ,     
    redis.call('publish', KEYS[2], ARGV[1]);   --    0         channel
    return 1;                                  --  1
end;
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then --RLock  ,            
    return nil;                                          --  nil redisson    :"attempt to unlock lock, not locked by current thread by node id: "+ id + " thread-id: " + Thread.currentThread().getId()
end;  
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);  --RLock  ,          ,      1
if (counter > 0) then                                         --            0
    redis.call('pexpire', KEYS[1], ARGV[2]);                    --         
    return 0;                                                   --  0
else                                                          --  
    redis.call('del', KEYS[1]);                                 --  RLock 
    redis.call('publish', KEYS[2], ARGV[1]);                    --    0         channel
    return 1;                                                   --  1
end;  
return nil;                                                   --  nil redisson    :"attempt to unlock lock, not locked by current thread by node id: "+ id + " thread-id: " + Thread.currentThread().getId()

forceUnlock:
強制ロック解除*–2つのキー、1つのパラメータ*keys="lock redisson_lock__channel__{lock}"*args="0"*{keys:lock->Rlockフラグredisson_lock__channel_{lock}->指定したチャンネルchannel(PUBLISH channel message、その他の情報はpublishコマンドを参照)args:0}
if (redis.call('del', KEYS[1]) == 1) then         --     
     redis.call('publish', KEYS[2], ARGV[1]);     --     0        channel
     return 1                                     --   1
else
     return 0                                     --   0
end

unlockAsync:
ロック解除(非同期)注記unlock*–2つのkey、3つのパラメータ*keys="*args=""
if (redis.call('exists', KEYS[1]) == 0) then  
    redis.call('publish', KEYS[2], ARGV[1]);  
    return 1;  
end;
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then  
    return nil;
end;  
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);  
if (counter > 0) then  
    redis.call('pexpire', KEYS[1], ARGV[2]);  
    return 0;  
else  
    redis.call('del', KEYS[1]);  
    redis.call('publish', KEYS[2], ARGV[1]);  
    return 1;
end;  
return nil;