一、分布式鎖介紹
單機多線程: 在 Java 中,我們通常使用 ReetrantLock 類、synchronized 關鍵字這類 本地鎖 來控制一個 JVM 進程內的多個線程對本地共享資源的訪問
分布式系統(tǒng): 不同的服務/客戶端通常運行在獨立的 JVM 進程上。如果多個 JVM 進程共享同一份資源的話,使用本地鎖就沒辦法實現(xiàn)資源的互斥訪問了。于是,分布式鎖就誕生了。
舉個例子:系統(tǒng)的訂單服務一共部署了 3 份,都對外提供服務。用戶下訂單之前需要檢查庫存,為了防止超賣,這里需要加鎖以實現(xiàn)對檢查庫存操作的同步訪問。由于訂單服務位于不同的 JVM 進程中,本地鎖在這種情況下就沒辦法正常工作了。我們需要用到分布式鎖,這樣的話,即使多個線程不在同一個 JVM 進程中也能獲取到同一把鎖,進而實現(xiàn)共享資源的互斥訪問。
一個最基本的分布式鎖需要滿足:
- 互斥 :任意一個時刻,鎖只能被一個線程持有;
- 高可用 :鎖服務是高可用的。并且,即使客戶端的釋放鎖的代碼邏輯出現(xiàn)問題,鎖最終一定還是會被釋放,不會影響其他線程對共享資源的訪問。
- 可重入:一個節(jié)點獲取了鎖之后,還可以再次獲取鎖。
二、基于 Redis 實現(xiàn)分布式鎖
1. 如何基于 Redis 實現(xiàn)一個最簡易的分布式鎖?
不論是本地鎖還是分布式鎖,核心都在于==“互斥”==。
在 Redis 中, SETNX
命令是可以幫助我們實現(xiàn)互斥。SETNX
即 SET if Not eXists (對應 Java 中的 setIfAbsent 方法),如果 key 不存在的話,才會設置 key 的值。如果 key 已經(jīng)存在, SETNX 啥也不做。
> SETNX lockKey uniqueValue
(integer) 1
> SETNX lockKey uniqueValue
(integer) 0
釋放鎖的話,直接通過 DEL 命令刪除對應的 key 即可
> DEL lockKey
(integer) 1
為了防止誤刪到其他的鎖,這里我們建議使用 Lua 腳本通過 key 對應的 value(唯一值)來判斷。
選用 Lua 腳本是為了保證解鎖操作的原子性。因為 Redis 在執(zhí)行 Lua 腳本時,可以以原子性的方式執(zhí)行,從而保證了鎖釋放操作的原子性。
// 釋放鎖時,先比較鎖對應的 value 值是否相等,避免鎖的誤釋放
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
這是一種最簡易的 Redis 分布式鎖實現(xiàn),實現(xiàn)方式比較簡單,性能也很高效。不過,這種方式實現(xiàn)分布式鎖存在一些問題。就比如應用程序遇到一些問題比如釋放鎖的邏輯突然掛掉,可能會導致鎖無法被釋放,進而造成共享資源無法再被其他線程/進程訪問。
2.為什么要給鎖設置一個過期時間?
主要為了避免鎖無法被釋放
127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK
- lockKey :加鎖的鎖名;
- uniqueValue :能夠唯一標示鎖的隨機字符串;
- NX :只有當 lockKey 對應的 key 值不存在的時候才能 SET 成功;
- EX :過期時間設置(秒為單位)EX 3 標示這個鎖有一個 3 秒的自動過期時間。與 EX 對應的是 PX(毫秒為單位),這兩個都是過期時間設置。
一定要保證設置指定 key 的值和過期時間是一個原子操作?。。?/strong> 不然的話,依然可能會出現(xiàn)鎖無法被釋放的問題。
這種解決辦法同樣存在漏洞:
- 如果操作共享資源的時間大于過期時間,就會出現(xiàn)鎖提前過期的問題,進而導致分布式鎖直接失效
- 如果鎖的超時時間設置過長,又會影響到性能
3. 如何實現(xiàn)鎖的優(yōu)雅續(xù)期?
Redisson 是一個開源的 Java 語言 Redis 客戶端,提供了很多開箱即用的功能,不僅僅包括多種分布式鎖的實現(xiàn)。并且,Redisson 還支持 Redis 單機、Redis Sentinel 、Redis Cluster 等多種部署架構。
Redisson 中的分布式鎖自帶自動續(xù)期機制,使用起來非常簡單,原理也比較簡單,其提供了一個專門用來監(jiān)控和續(xù)期鎖的 Watch Dog( 看門狗),如果操作共享資源的線程還未執(zhí)行完成的話,Watch Dog 會不斷地延長鎖的過期時間,進而保證鎖不會因為超時而被釋放。
使用方式舉例:
// 1.獲取指定的分布式鎖對象
RLock lock = redisson.getLock("lock");
// 2.拿鎖且不設置鎖超時時間,具備 Watch Dog 自動續(xù)期機制
lock.lock();
// 3.執(zhí)行業(yè)務
...
// 4.釋放鎖
lock.unlock();
只有未指定鎖超時時間,才會使用到 Watch Dog 自動續(xù)期機制。
// 手動給鎖設置過期時間,不具備 Watch Dog 自動續(xù)期機制
lock.lock(10, TimeUnit.SECONDS);
總的來說就是使用Redisson,它帶有自動的續(xù)期機制
4. 如何實現(xiàn)可重入鎖?
所謂可重入鎖指的是在一個線程中可以多次獲取同一把鎖,比如一個線程在執(zhí)行一個帶鎖的方法,該方法中又調用了另一個需要相同鎖的方法,則該線程可以直接執(zhí)行調用的方法即可重入 ,而無需重新獲得鎖。像 Java 中的 synchronized 和 ReentrantLock 都屬于可重入鎖。
可重入分布式鎖的實現(xiàn)核心思路是線程在獲取鎖的時候判斷是否為自己的鎖,如果是的話,就不用再重新獲取了。為此,我們可以為每個鎖關聯(lián)一個可重入計數(shù)器和一個占有它的線程。當可重入計數(shù)器大于 0 時,則鎖被占有,需要判斷占有該鎖的線程和請求獲取鎖的線程是否為同一個。
三、基于 Zookeeper 實現(xiàn)分布式鎖
1. Zookeeper概念
Zookeeper是 Apache Hadoop項目下的一個子項目。
Zookeeper 是一個為分布式應用提供一致性服務的軟件,例如配置管理、分布式協(xié)同以及命名的中心化等,這些都是分布式系統(tǒng)中非常底層而且是必不可少的基本功能,但是如果自己實現(xiàn)這些功能而且要達到高吞吐、低延遲同時還要保持一致性和可用性,實際上非常困難,所以這時可以使用zookeeper來幫助實現(xiàn)。
- 數(shù)據(jù)模型
數(shù)據(jù)模型如下圖,zookeeper提供了一種樹形結構級的命名空間,/app1/p_1 節(jié)點表示它的父節(jié)點為 /app1
-
節(jié)點類型
永久節(jié)點:不會因為會話結束或者超時而消失;
臨時節(jié)點:如果會話結束或者超時就會消失;
有序節(jié)點:會在節(jié)點名的后面加一個數(shù)字后綴,并且是有序的,例如生成的有序節(jié)點為 /lock/node-0000000000,它的下一個有序節(jié)點則為 /lock/node-0000000001,依次類推。
-
監(jiān)聽器
為一個節(jié)點注冊監(jiān)聽器,在節(jié)點狀態(tài)發(fā)生改變時,會給客戶端發(fā)送消息
2. Zookeeper主要功能
- 配置管理:多個服務器使用同一配置時,對配置修改需要一個一個修改,為了避免這種情況,可以將配置放置在配置中心中,服務器有需要可以進行拉取,zookeeper即可實現(xiàn)配置中心的功能,除此之外常見的配置中心還有阿里的nacos等
- 集群管理:如服務的注冊中心等等,管理服務提供方的ip地址端口號url信息,并在服務消費方請求需要時發(fā)送給服務消費方,在微服務架構中常見,當然除了zookeeper也可以用nacos
- 分布式鎖
3. Zookeeper作為分布式鎖的實現(xiàn)
根據(jù)上面介紹的zookeeper的特性,可以方便地實現(xiàn)分布式鎖。
實現(xiàn)方式:文章來源:http://www.zghlxwxcb.cn/news/detail-626829.html
- 創(chuàng)建一個鎖目錄 /lock;
- 在 /lock 下創(chuàng)建臨時的且有序的子節(jié)點,第一個客戶端對應的子節(jié)點為 /lock/lock-0000000000,第二個為 /lock/lock-0000000001,以此類推;
- 客戶端獲取 /lock 下的子節(jié)點列表,判斷自己創(chuàng)建的子節(jié)點是否為當前子節(jié)點列表中序號最小的子節(jié)點,如果是則認為獲得鎖;
- 否則監(jiān)聽自己的前一個子節(jié)點,獲得子節(jié)點的變更通知后重復此步驟直至獲得鎖;
- 執(zhí)行業(yè)務代碼,完成后,刪除對應的子節(jié)點。
會話超時的情況:如果一個已經(jīng)獲得鎖的會話超時了,因為創(chuàng)建的是臨時節(jié)點,所以該會話對應的臨時節(jié)點會被刪除,其它會話就可以獲得鎖了文章來源地址http://www.zghlxwxcb.cn/news/detail-626829.html
到了這里,關于【分布式】分布式鎖的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!