redis分布式鎖在加鎖的時候,我們一般都會給乙個鎖的過期時間(ttl),這是為了防止加鎖後client宕機,鎖無法被釋放的問題。但是所有這種姿勢的用法都會面臨同乙個問題,就是沒發保證client的執行時間一定小於鎖的ttl。雖然大多數程式設計師都會樂觀的認為這種情況不可能發生,但是各種異常情況都會導致該問題的發生,比如網路延遲,jvm full gc。
martin kleppmann也質疑過這一點,這裡直接用他的圖:
client1獲取到鎖
client1開始任務,然後發生了stw的gc,時間超過了鎖的過期時間
client2 獲取到鎖,開始了任務
client1的gc結束,繼續任務,這個時候client1和client2都認為自己獲取了鎖,都會處理任務,從而發生錯誤。
如何解決呢?
可以給鎖設定乙個watchdog自動給鎖進行續期。實現的原理就是在加鎖成功之後啟動乙個定時執行緒(watchdog)自動給鎖進行續期。
// org.redisson.redissonlock#tryacquireasync()
private rfuturetryacquireasync(long leasetime, timeunit unit, long threadid)
rfuturettlremainingfuture = trylockinnerasync(
commandexecutor.getconnectionmanager().getcfg().getlockwatchdogtimeout(),
timeunit.milliseconds, threadid, rediscommands.eval_long);
// 非同步獲取結果,如果獲取鎖成功,則啟動定時執行緒進行鎖續約
ttlremainingfuture.oncomplete((ttlremaining, e) ->
// lock acquired
if (ttlremaining == null)
});return ttlremainingfuture;
}
// org.redisson.redissonlock#scheduleexpirationrenewal()
private static final concurrentmapexpiration_renewal_map =
new concurrenthashmap<>();
private void scheduleexpirationrenewal(long threadid) else
}
// org.redisson.redissonlock#renewexpiration()
private void renewexpiration()
timeout task = commandexecutor.getconnectionmanager().newtimeout(new timertask()
long threadid = ent.getfirstthreadid();
if (threadid == null)
// renewexpirationasync續約租期
rfuturefuture = renewexpirationasync(threadid);
future.oncomplete((res, e) ->
if (res)
});}
//每次間隔租期的1/3時間執行
}, internallockleasetime / 3, timeunit.milliseconds);
ee.settimeout(task);
}
// org.redisson.redissonlock#renewexpirationasync()
protected rfuturerenewexpirationasync(long threadid)
watchdog其實就是乙個週期任務,給出乙個更加簡單的例子:
private void startwatchdog(string lockname, string lockvalue, int expiredtime) else
}))).start();
}public class schedulelockwatchdogtask implements scheduletask
@override
public void executetask()
}
分布式 分布式鎖
本質是利用redis的setnx 方法的特性來加鎖,setnx 即key不存在則設定key,否則直接返回false,要求在分布式系統中使用同乙個redis服務,以下提供兩種解決方案 1 直接使用redistemplate 這其實並不能完全保證高併發下的安全問題,因為可能在鎖過期之後該執行緒尚未執行完...
分布式專題 分布式鎖
在傳統的單體應用架構中,遇到併發安全性問題時我們可以通過同步鎖synchronized,同步 塊,reentrantlock等方式都可以解決,但隨著業務的發展,單體應用架構不能滿足龐大的使用者請求量,於是分布式系統應用而生,在分布式系統中,由於每個系統都執行在不同的伺服器上,有著不同的jvm,所以j...
zookeeper(3)分布式鎖
在乙個分布式系統中,如何保證乙個操作,同一時間只有乙個執行緒可以執行,這就是分布式鎖的使用場景,同一時間,只有乙個執行緒可以獲得鎖的使用權。實現乙個分布式鎖,可以有以下3種方法。1 在mysql中,使用悲觀鎖 select from t where id for update 可以對行資料進行加鎖,...