分布式鎖的封裝 原來這麼多講究

2021-10-25 07:16:27 字數 2484 閱讀 8382

分布式鎖通常有很多選擇,基於 redis 的,基於 zookeeper 的,基於資料庫等等方案。

redis 用於快取資料,在專案中都有使用,所以使用 redis 來做分布式鎖的會稍微多些。

如果用 redis 來做鎖,可以直接用開源的方案,比如redisson。

最常見的使用方式如下所示:

rlock lock = redisson.getlock("anylock");

lock.lock();

run();

lock.unlock();

獲取鎖物件,呼叫 lock()加鎖,執行業務邏輯,呼叫 unlock()釋放鎖。

儘管框架提供的使用方式已經很簡潔了,但是我們還是有必要對鎖做一層包裝。做包裝的目的是為了提高擴充套件性和易用性。

如果說我們直接使用 redisson 的原生 api 做加鎖,那麼很多地方都會出現 rlock 相關的**,突然有一天,由於某些原因,需要將鎖進行替換,這個時候改動的範圍就比較大了。每個使用了 rlock 的地方都得改。

如下圖:很多 service 都用到了 rlock.lock()方法,當我們需要替換鎖的時候,所有涉及到的類和方法都得修改,改動的點如紅色部分所示。

所以我們需要做一層抽象,可以定義乙個 distributedlock 介面來提供鎖相關的能力,提供多種實現,這樣方便替換和擴充套件。

如下圖:每個 service 中都是用的 distributedlock 介面來加鎖,當我們需要替換鎖的實現時,使用的地方不需要改動,只需要替換 distributedlock 的實現即可。

自動釋放指的是對於加鎖之後,業務邏輯執行完畢需要自動關閉鎖。按照前面 redisson 的方式我們需要手動呼叫 unlock()來釋放持有的鎖。

當然 redisson 也提供了超時釋放的功能,正常情況下肯定是業務執行完畢就要釋放鎖了,同乙個鎖的下個請求才能繼續接著處理。

手動釋放資源最容易出現的問題就是忘記釋放,所以在 jdk7 中引入了 try-with-resources 來自動釋放資源,相信大家都很熟悉。

所以我們在封裝的時候,盡量不要讓使用者去手動釋放,減少出錯的概率。對於有結果的我們可以使用 supplier 來傳遞你的邏輯,對於沒有返回結果的可以用 runnable 來傳遞你的邏輯。

/**

* 加鎖

* @param key 鎖key

* @param waittime 嘗試加鎖,等待時間 (ms)

* @param leasetime 上鎖後的失效時間 (ms)

* @param success 鎖成功執行的邏輯

* @param fail 鎖失敗執行的邏輯

* @return

*/t lock(string key, int waittime, int leasetime, suppliersuccess, supplie***il);

使用:

string result = distributedlock.lock("1001", 1000, () ->  catch (interruptedexception e) 

return "success";

}, () -> );

另乙個需要注意的問題就是鎖的可用性,萬一對應的 redis 出問題了,這個時候去加鎖肯定會失敗,如果不做任何處理,就會影響正常的業務操作,導致業務不可用。

我們除了實現 redis 的鎖之外,還可以實現其他的鎖,比如資料庫鎖。當 redis 鎖不可用的時候降級為資料庫鎖,雖然效能有所影響,但是不會影響業務。

加鎖流程

如果資料庫鎖也不可用了(題外話:所有都不可用可能性非常小),那還是讓業務操作失敗比較好。因為我們用加鎖的場景,肯定是為了防止併發場景帶來的問題,如果當鎖不可用時,你將異常消費了,讓業務操作繼續下去,就有可能出現沒有加鎖的業務問題。

當然監控也非常需要,redis, 資料庫等監控。在出故障的時候,及時有人員介入。

redis, 資料庫,zookeeper 這些承載分布式實現的中介軟體的監控肯定是必須要有的。另乙個監控就是更細粒度的對應鎖這個動作的監控。

比如加鎖的時間,釋放鎖的時間,在鎖裡面執行業務的時間,鎖的併發量,執行次數,加鎖失敗的次數。

這些資料指標都非常重要,能夠幫助你及時發現問題。比如 10 秒內幾百次加鎖失敗,都降級成了資料庫鎖,這個時候你收到了警報,一看就知道 redis 出問題了,及時解決。

監控方式就隨便了,每個公司都不一樣,你可以暴露資料給 prometheus 抓取,也可以整合 cat 做好埋點,只要能監控,能告警就可以了。

分布式事務,原來可以這麼玩?

事務,是常見的做法。餘額表,訂單表 流水表 於是會有類似的偽 如果對餘額表,訂單表,流水表的sql操作全部成功,則全部提交 如果任何乙個出現問題,則全部回滾 網際網路的業務特點,資料量較大,併發量較大,經常使用 拆庫的方式提公升系統的效能。如果進行了拆庫,餘額 訂單 流水可能分布在不同的資料庫 上,...

分布式鎖的鎖優化

在去除原有synchronized單機鎖後,在關鍵步驟新增分布式鎖來對具體業務進行鎖定,然而由於鎖定範圍大,導致鎖競爭增加,不斷發生鎖等待,如果不進行優化,可能會讓執行緒佇列增大甚至阻塞,而且在等待時長超過設定的閾值時,執行緒將超時返回。在此,初步對鎖進行優化,如何理解分布式鎖與單機鎖的應用範圍和實...

分布式鎖的實現

分布式鎖的實現方式通常有三種,第一種是基於資料庫實現分布式鎖,第二種是基於快取實現分布式鎖,第三種是基於zookeeper實現分布式鎖.第一種 基於資料庫實現分布式鎖 特點 效能較差,容易出現單點故障 鎖沒有失效時間,容易思死鎖 非阻塞式的 不可重入 第二種基於快取實現分布式鎖 鎖沒有失效時間,容易...