新聞中心
分布式鎖的封裝也很有講究呀
作者:佚名 2020-10-14 11:50:10
數(shù)據(jù)庫(kù)
其他數(shù)據(jù)庫(kù)
分布式 分布式鎖通常有很多選擇,基于 Redis 的,基于 Zookeeper 的,基于數(shù)據(jù)庫(kù)等等方案。Redis 用于緩存數(shù)據(jù),在項(xiàng)目中都有使用,所以使用 Redis 來(lái)做分布式鎖的會(huì)稍微多些。

在原州等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專(zhuān)注、極致的服務(wù)理念,為客戶提供網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作定制設(shè)計(jì),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站制作,營(yíng)銷(xiāo)型網(wǎng)站,成都外貿(mào)網(wǎng)站制作,原州網(wǎng)站建設(shè)費(fèi)用合理。
分布式鎖通常有很多選擇,基于 Redis 的,基于 Zookeeper 的,基于數(shù)據(jù)庫(kù)等等方案。
Redis 用于緩存數(shù)據(jù),在項(xiàng)目中都有使用,所以使用 Redis 來(lái)做分布式鎖的會(huì)稍微多些。
如果用 Redis 來(lái)做鎖,可以直接用開(kāi)源的方案,比如redisson。
最常見(jiàn)的使用方式如下所示:
- RLock lock = redisson.getLock("anyLock");
- lock.lock();
- run();
- lock.unlock();
獲取鎖對(duì)象,調(diào)用 lock()加鎖,執(zhí)行業(yè)務(wù)邏輯,調(diào)用 unlock()釋放鎖。
盡管框架提供的使用方式已經(jīng)很簡(jiǎn)潔了,但是我們還是有必要對(duì)鎖做一層包裝。做包裝的目的是為了提高擴(kuò)展性和易用性。
抽象接口
如果說(shuō)我們直接使用 redisson 的原生 API 做加鎖,那么很多地方都會(huì)出現(xiàn) RLock 相關(guān)的代碼,突然有一天,由于某些原因,需要將鎖進(jìn)行替換,這個(gè)時(shí)候改動(dòng)的范圍就比較大了。每個(gè)使用了 RLock 的地方都得改。
如下圖:很多 Service 都用到了 RLock.lock()方法,當(dāng)我們需要替換鎖的時(shí)候,所有涉及到的類(lèi)和方法都得修改,改動(dòng)的點(diǎn)如紅色部分所示。
所以我們需要做一層抽象,可以定義一個(gè) DistributedLock 接口來(lái)提供鎖相關(guān)的能力,提供多種實(shí)現(xiàn),這樣方便替換和擴(kuò)展。
如下圖:每個(gè) Service 中都是用的 DistributedLock 接口來(lái)加鎖,當(dāng)我們需要替換鎖的實(shí)現(xiàn)時(shí),使用的地方不需要改動(dòng),只需要替換 DistributedLock 的實(shí)現(xiàn)即可。
自動(dòng)釋放
自動(dòng)釋放指的是對(duì)于加鎖之后,業(yè)務(wù)邏輯執(zhí)行完畢需要自動(dòng)關(guān)閉鎖。按照前面 Redisson 的方式我們需要手動(dòng)調(diào)用 unlock()來(lái)釋放持有的鎖。
當(dāng)然 Redisson 也提供了超時(shí)釋放的功能,正常情況下肯定是業(yè)務(wù)執(zhí)行完畢就要釋放鎖了,同一個(gè)鎖的下個(gè)請(qǐng)求才能繼續(xù)接著處理。
手動(dòng)釋放資源最容易出現(xiàn)的問(wèn)題就是忘記釋放,所以在 JDK7 中引入了 try-with-resources 來(lái)自動(dòng)釋放資源,相信大家都很熟悉。
所以我們?cè)诜庋b的時(shí)候,盡量不要讓使用者去手動(dòng)釋放,減少出錯(cuò)的概率。對(duì)于有結(jié)果的我們可以使用 Supplier 來(lái)傳遞你的邏輯,對(duì)于沒(méi)有返回結(jié)果的可以用 Runnable 來(lái)傳遞你的邏輯。
- /**
- * 加鎖
- * @param key 鎖Key
- * @param waitTime 嘗試加鎖,等待時(shí)間 (ms)
- * @param leaseTime 上鎖后的失效時(shí)間 (ms)
- * @param success 鎖成功執(zhí)行的邏輯
- * @param fail 鎖失敗執(zhí)行的邏輯
- * @return
- */
T lock(String key, int waitTime, int leaseTime, Supplier success, Supplier fail);
使用:
- String result = distributedLock.lock("1001", 1000, () -> {
- System.out.println("進(jìn)來(lái)了。。。。");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return "success";
- }, () -> {
- System.out.println("加鎖失敗。。。。");
- return "fail";
- });
容災(zāi)處理
另一個(gè)需要注意的問(wèn)題就是鎖的可用性,萬(wàn)一對(duì)應(yīng)的 Redis 出問(wèn)題了,這個(gè)時(shí)候去加鎖肯定會(huì)失敗,如果不做任何處理,就會(huì)影響正常的業(yè)務(wù)操作,導(dǎo)致業(yè)務(wù)不可用。
我們除了實(shí)現(xiàn) Redis 的鎖之外,還可以實(shí)現(xiàn)其他的鎖,比如數(shù)據(jù)庫(kù)鎖。當(dāng) Redis 鎖不可用的時(shí)候降級(jí)為數(shù)據(jù)庫(kù)鎖,雖然性能有所影響,但是不會(huì)影響業(yè)務(wù)。
加鎖流程
如果數(shù)據(jù)庫(kù)鎖也不可用了(題外話:所有都不可用可能性非常小),那還是讓業(yè)務(wù)操作失敗比較好。因?yàn)槲覀冇眉渔i的場(chǎng)景,肯定是為了防止并發(fā)場(chǎng)景帶來(lái)的問(wèn)題,如果當(dāng)鎖不可用時(shí),你將異常消費(fèi)了,讓業(yè)務(wù)操作繼續(xù)下去,就有可能出現(xiàn)沒(méi)有加鎖的業(yè)務(wù)問(wèn)題。
當(dāng)然監(jiān)控也非常需要,Redis, 數(shù)據(jù)庫(kù)等監(jiān)控。在出故障的時(shí)候,及時(shí)有人員介入。
監(jiān)控體系
Redis, 數(shù)據(jù)庫(kù),Zookeeper 這些承載分布式實(shí)現(xiàn)的中間件的監(jiān)控肯定是必須要有的。另一個(gè)監(jiān)控就是更細(xì)粒度的對(duì)應(yīng)鎖這個(gè)動(dòng)作的監(jiān)控。
比如加鎖的時(shí)間,釋放鎖的時(shí)間,在鎖里面執(zhí)行業(yè)務(wù)的時(shí)間,鎖的并發(fā)量,執(zhí)行次數(shù),加鎖失敗的次數(shù)。
這些數(shù)據(jù)指標(biāo)都非常重要,能夠幫助你及時(shí)發(fā)現(xiàn)問(wèn)題。比如 10 秒內(nèi)幾百次加鎖失敗,都降級(jí)成了數(shù)據(jù)庫(kù)鎖,這個(gè)時(shí)候你收到了警報(bào),一看就知道 Redis 出問(wèn)題了,及時(shí)解決。
監(jiān)控方式就隨便了,每個(gè)公司都不一樣,你可以暴露數(shù)據(jù)給 Prometheus 抓取,也可以集成 Cat 做好埋點(diǎn),只要能監(jiān)控,能告警就可以了。
分享題目:分布式鎖的封裝也很有講究呀
網(wǎng)頁(yè)鏈接:http://www.dlmjj.cn/article/dhcoheg.html


咨詢
建站咨詢
