Redisson
Redisson是一個在Redis的基礎上實現的Java駐內存數據網格(In-Memory Data Grid)。
Redisson不僅提供了一系列的分布式的Java常用對象,還提供了許多分布式服務。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service), Redisson提供了使用Redis的最簡單和最便捷的方法。
Redisson的宗旨是促進使用者對Redis的關注分離(Separation of Concern),從而讓使用者能夠將精力更集中地放在處理業務邏輯上。
lua代碼實現
通過封裝在lua腳本中發送給redis,保證這段復雜業務邏輯執行的原子性。
加鎖
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
釋放鎖
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;
Redisson實現分布式鎖
可重入鎖
RLock lock = redisson.getLock("lock");
lock.lock();
lock.unlock()
// 加鎖以后10秒鍾自動解鎖
// 無需調用unlock方法手動解鎖
lock.lock(10, TimeUnit.SECONDS);
// 嘗試加鎖,最多等待100秒,上鎖以后10秒自動解鎖
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
公平鎖
RLock fairLock = redisson.getFairLock("anyLock");
// 最常見的使用方法
fairLock.lock();
// 10秒鍾以后自動解鎖
// 無需調用unlock方法手動解鎖
fairLock.lock(10, TimeUnit.SECONDS);
// 嘗試加鎖,最多等待100秒,上鎖以后10秒自動解鎖
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();
watch dog自動延期機制
Redisson中客戶端一旦加鎖成功,就會啟動一個watch dog看門狗。watch dog是一個后台線程,會每隔10秒檢查一下,如果客戶端還持有鎖key,那么就會不斷的延長鎖key的生存時間。
如果負責存儲這個分布式鎖的Redission節點宕機后,而且這個鎖正好處於鎖住的狀態時,這個鎖會出現鎖死的狀態,為了避免這種情況的發生,Redisson提供了一個監控鎖的看門狗,它的作用是在Redisson實例被關閉前,不斷的延長鎖的有效期。默認情況下,看門狗的續期時間是30s,也可以通過修改Config.lockWatchdogTimeout來另行指定。
另外Redisson 還提供了可以指定leaseTime參數的加鎖方法來指定加鎖的時間。超過這個時間后鎖便自動解開了,不會延長鎖的有效期。
缺點
對某個redis master實例,寫入了myLock這種鎖key的value,此時會異步復制給對應的master slave實例。但是這個過程中一旦發生redis master宕機,主備切換,redis slave變為了redis master。就會導致客戶端2來嘗試加鎖的時候,在新的redis master上完成了加鎖,而客戶端1也以為自己成功加了鎖。
此時就會導致多個客戶端對一個分布式鎖完成了加鎖。這時系統在業務上一定會出現問題,導致臟數據的產生。所以這個就是redis cluster,或者是redis master-slave架構的主從異步復制導致的redis分布式鎖的最大缺陷:在redis master實例宕機的時候,可能導致多個客戶端同時完成加鎖。