1. 程式人生 > >分散式鎖原始碼剖析(3) Redisson的MultiLock和RedLock

分散式鎖原始碼剖析(3) Redisson的MultiLock和RedLock

MultiLock

MultiLock:將多個鎖合併為一個大鎖,對一個大鎖進行統一的申請加鎖以及釋放鎖,一次性鎖定多個資源,再去處理一些事情,然後事後一次性釋放所有的資源對應的鎖。

maven配置檔案:

 <dependency>
         <groupId>org.redisson</groupId>
         <artifactId>redisson</artifactId>
         <version>3.8.1</version>
</dependency>

程式碼示例:

Config config = new Config();
Config config = new Config();
config.useClusterServers()
    .addNodeAddress("redis://192.168.31.114:7001")
    .addNodeAddress("redis://192.168.31.184:7002");

RedissonClient redisson = Redisson.create(config);

RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// locks: lock1 lock2 lock3
lock.lock();
lock.unlock();

核心原始碼:

根據鎖的個數,計算出基礎的等待時間。

long baseWaitTime = locks.size() * 1500;

迴圈嘗試獲取鎖,直到獲取鎖成功後返回。

while (true) {
    if (tryLock(waitTime, leaseTime, unit)) {
        return;
    }
}

呼叫RedissonLock的tryLock方法,嘗試獲取鎖,如果獲取鎖成功,把鎖新增到集合。

List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());
lockAcquired = lock.tryLock();
acquiredLocks.add(lock);

如果獲取鎖超時就會退出,重新再次死迴圈嘗試獲取鎖。

if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
    break;
}

if (failedLocksLimit == 0) {
    unlockInner(acquiredLocks);
    if (waitTime == -1 && leaseTime == -1) {
        return false;
    }
    failedLocksLimit = failedLocksLimit();
    acquiredLocks.clear();
    // reset iterator
    while (iterator.hasPrevious()) {
        iterator.previous();
    }
} else {
    failedLocksLimit--;
}

RedLock

maven配置檔案:

 <dependency>
         <groupId>org.redisson</groupId>
         <artifactId>redisson</artifactId>
         <version>3.8.1</version>
</dependency>

程式碼示例:

Config config = new Config();
config.useClusterServers()
    .addNodeAddress("redis://192.168.31.114:7001")
    .addNodeAddress("redis://192.168.31.184:7002");

RedissonClient redisson = Redisson.create(config);

RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// locks: lock1 lock2 lock3
lock.lock();
lock.unlock();

演算法原理:

1、獲取當前時間戳,單位是毫秒。

2、跟上面類似,輪流嘗試在每個master節點上建立鎖,過期時間較短,一般就幾十毫秒,在每個節點上建立鎖的過程中,需要加一個超時時間,一般來說比如幾十毫秒如果沒有獲取到鎖就超時了,標識為獲取鎖失敗。

3、嘗試在大多數節點上建立一個鎖,比如3個節點就要求是2個節點(n / 2 +1)。

4、客戶端計算建立好鎖的時間,如果建立鎖的時間小於超時時間,就算建立成功了。

5、要是鎖建立失敗了,那麼就依次刪除已經建立的鎖。

6、只要別人建立了一把分散式鎖,你就得不斷輪詢去嘗試獲取鎖。

核心原始碼:

是RedissonMultiLock的一個子類,RedLock演算法的實現,是依賴於MultiLock的一個機制來實現。主要通過重寫failedLocksLimit和calcLockWaitTime實現,只有n/2+1個加鎖操作失敗之後,才會重新去迴圈加鎖。

public class RedissonRedLock extends RedissonMultiLock {

    public RedissonRedLock(RLock... locks) {
        super(locks);
    }

    @Override
    protected int failedLocksLimit() {
        return locks.size() - minLocksAmount(locks);
    }
    
    protected int minLocksAmount(final List<RLock> locks) {
        return locks.size()/2 + 1;
    }

    @Override
    protected long calcLockWaitTime(long remainTime) {
        return Math.max(remainTime / locks.size(), 1);
    }
    
    @Override
    public void unlock() {
        unlockInner(locks);
    }
}