您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“如何使用分布式鎖”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何使用分布式鎖”吧!
什么是分布式鎖?分布式鎖又可以解決哪些問(wèn)題呢?
在我們的系統(tǒng)還沒(méi)有使用分布式架構(gòu)的時(shí)候,我們可以用同步鎖或者Lock鎖,來(lái)保證多線程并發(fā)的時(shí)候,同一時(shí)間只有一個(gè)線程修改共享變量或者執(zhí)行代碼塊,但是當(dāng)我們現(xiàn)在大部分系統(tǒng)都是分布式集群部署的,單純的同步鎖和Lock鎖只能保證單個(gè)實(shí)例上的數(shù)據(jù)一致性,多實(shí)例就失去了作用。
這個(gè)時(shí)候就需要使用分布式鎖來(lái)保證共享資源的原子性,比如我們電商系統(tǒng)里面的扣減庫(kù)存,當(dāng)單量小的時(shí)候問(wèn)題不大,如果單量很大,同一時(shí)間多個(gè)實(shí)例都在并發(fā)處理扣減庫(kù)存的業(yè)務(wù)的時(shí)候,就可能存在超賣(mài)的問(wèn)題。
分布式鎖的實(shí)現(xiàn)?
常見(jiàn)的分布式鎖有數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖、Zookeeper實(shí)現(xiàn)分布式鎖、Redis實(shí)現(xiàn)分布式鎖、Redisson實(shí)現(xiàn)。其中數(shù)據(jù)庫(kù)實(shí)現(xiàn)分布式鎖比較簡(jiǎn)單,也很容易理解,直接基于數(shù)據(jù)庫(kù)實(shí)現(xiàn)就可以了,在一些分布式的業(yè)務(wù)中也經(jīng)常使用,但是這種方式也是效率最低的,一般是不使用的,我們就著重介紹一下其他三種方式的實(shí)現(xiàn)。
Zookeeper實(shí)現(xiàn)分布式鎖
使用Zookeeper來(lái)實(shí)現(xiàn)分布式鎖就比較常見(jiàn),比如很多項(xiàng)目就使用Zookeeper作為分布式注冊(cè)中心,就喜歡用Zookeeper來(lái)實(shí)現(xiàn)分布式鎖,這主要是借助于Zookeeper的兩大特性:順序臨時(shí)節(jié)點(diǎn)、Watch機(jī)制。
順序臨時(shí)節(jié)點(diǎn):熟悉Zookeeper的同學(xué)都知道,Zookeeper提供了多層級(jí)的節(jié)點(diǎn)命名空間,每個(gè)節(jié)點(diǎn)都是用斜杠分隔的路徑來(lái)表示,類似于我們的文件夾。節(jié)點(diǎn)又分為持久節(jié)點(diǎn)和臨時(shí)節(jié)點(diǎn),節(jié)點(diǎn)還可以標(biāo)記為有序,當(dāng)節(jié)點(diǎn)被標(biāo)記為有序性,這個(gè)節(jié)點(diǎn)就具有順序自增的特點(diǎn),我們就可以借助這個(gè)特點(diǎn)來(lái)創(chuàng)建我們所需的節(jié)點(diǎn)。
Watch機(jī)制:Watch機(jī)制是Zookeeper另一個(gè)重要的特性,我們可以在指定節(jié)點(diǎn)上注冊(cè)一些Watcher,在一些特定的事情觸發(fā)的時(shí)候,通知用戶這個(gè)事件。
Zookeeper實(shí)現(xiàn)分布式鎖的過(guò)程
我們先創(chuàng)建一個(gè)持久節(jié)點(diǎn)作為父節(jié)點(diǎn),每當(dāng)需要訪問(wèn)創(chuàng)建分布式鎖的時(shí)候,就在這個(gè)父節(jié)點(diǎn)下創(chuàng)建相應(yīng)的臨時(shí)的順序子節(jié)點(diǎn),以臨時(shí)節(jié)點(diǎn)名稱、父節(jié)點(diǎn)名稱和順序號(hào)組成特點(diǎn)的名稱。在建立子節(jié)點(diǎn)后,對(duì)父節(jié)點(diǎn)下以這個(gè)這個(gè)子節(jié)點(diǎn)名稱開(kāi)頭的子節(jié)點(diǎn)進(jìn)行排序,判斷剛建立的節(jié)點(diǎn)順序號(hào)是不是最小的,如果是最小的則獲取鎖,如果不是最小節(jié)點(diǎn),則阻塞等待鎖,并且在獲取該節(jié)點(diǎn)的上一順序節(jié)點(diǎn)注冊(cè)Watcher,等待節(jié)點(diǎn)對(duì)應(yīng)的操作獲得鎖。
當(dāng)業(yè)務(wù)處理完之后,刪除該節(jié)點(diǎn),關(guān)閉zk,進(jìn)而觸發(fā)Watcher,釋放該鎖。
上圖就是就是嚴(yán)格按照順序訪問(wèn)的分布式鎖實(shí)現(xiàn),更多的時(shí)候我們引入一些框架來(lái)幫助我們實(shí)現(xiàn),比如最常用的Curator框架,代碼如下:
InterProcessMutex lock = new InterProcessMutex(client, lockPath); if ( lock.acquire(maxWait, waitUnit) ) { try { // 業(yè)務(wù)處理 } finally{ lock.release(); } }
Zookeeper來(lái)實(shí)現(xiàn)分布式鎖天然的優(yōu)勢(shì)就是,Zookeeper是集群實(shí)現(xiàn)的,我們生產(chǎn)環(huán)境一般也是集群部署的,可以避免單點(diǎn)問(wèn)題,穩(wěn)定性較好,能保證每次操作都可以釋放鎖。
缺點(diǎn)就是,頻繁的創(chuàng)建刪除節(jié)點(diǎn),加上注冊(cè)watch事件,對(duì)于zookeeper集群的壓力比較大,性能這一塊也比不上Redis實(shí)現(xiàn)的分布式鎖。
Redis實(shí)現(xiàn)分布式鎖
Redis實(shí)現(xiàn)的分布式鎖,最為復(fù)雜,但是性能確是最佳的,所以在對(duì)性能要求更高的系統(tǒng)里,我們都選擇使用Redis來(lái)實(shí)現(xiàn)分布式鎖。利用Redis實(shí)現(xiàn)分布式鎖,一般都是使用SETNX實(shí)現(xiàn),舉個(gè)簡(jiǎn)單的例子:
public static boolean getDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if ("OK".equals(result)) { return true; } return false; }
SETNX方法保證設(shè)置鎖和鎖過(guò)期時(shí)間的原子性,但是對(duì)于鎖的過(guò)期時(shí)間設(shè)置我們要注意,如果執(zhí)行業(yè)務(wù)
時(shí)間比較長(zhǎng),我們?cè)O(shè)置的過(guò)期時(shí)間又比較短的情況下就會(huì)造成,業(yè)務(wù)還沒(méi)執(zhí)行完,鎖已釋放的問(wèn)題。所以我們需要根據(jù)實(shí)際業(yè)務(wù)處理來(lái)評(píng)估設(shè)置鎖的過(guò)期時(shí)間,來(lái)保證業(yè)務(wù)可以正常的處理完。
Redisson實(shí)現(xiàn)分布式鎖
Redisson是架設(shè)在Redis基礎(chǔ)上的一個(gè)Java駐內(nèi)存數(shù)據(jù)網(wǎng)格。Redisson在基于NIO的Netty框架上,充分的利用了Redis鍵值數(shù)據(jù)庫(kù)提供的一系列優(yōu)勢(shì),在Java實(shí)用工具包中常用接口的基礎(chǔ)上,為使用者提供了一系列具有分布式特性的常用工具類。性能也比我們常用的jedis好一些。
Redisson不管是單節(jié)點(diǎn)模式還是集群模式,都很好的實(shí)現(xiàn)了分布式鎖,一般用的多的都是集群模式,在集群模式下,Redisson使用RedLock算法,很好的處理了Master節(jié)點(diǎn)宕機(jī)時(shí)切換到另外一個(gè)Master節(jié)點(diǎn)過(guò)程中多個(gè)應(yīng)用獲得鎖。
Redisson集群模式獲取鎖的實(shí)現(xiàn)就是,在不同節(jié)點(diǎn)上獲取鎖,每個(gè)節(jié)點(diǎn)上獲取鎖都有超時(shí)時(shí)間,如果獲取鎖超時(shí)就認(rèn)為這個(gè)節(jié)點(diǎn)不可用,當(dāng)成功獲取鎖的個(gè)數(shù)超過(guò)Redis節(jié)點(diǎn)的半數(shù),且獲取鎖消耗的時(shí)間還沒(méi)超過(guò)鎖過(guò)期時(shí)間,則認(rèn)為獲取鎖成功。獲取鎖成功后重新計(jì)算鎖釋放時(shí)間,由原來(lái)的鎖釋放時(shí)間減去獲取鎖消耗的時(shí)間,如果最終獲取鎖失敗,已經(jīng)獲取鎖成功的節(jié)點(diǎn)也會(huì)釋放鎖。
具體的代碼實(shí)現(xiàn):
引入依賴
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.1</version> </dependency>
Redisson配置文件:
@Bean public RedissonClient redissonClient() { Config config = new Config(); config.useClusterServers() .setScanInterval(3000) // 集群狀態(tài)掃描間隔時(shí)間,單位是毫秒 .addNodeAddress("redis://192.168.0.1:6379).setPassword("666") .addNodeAddress("redis://192.168.0.2:6379").setPassword("666") .addNodeAddress("redis://192.168.0.3:6379") .setPassword("666"); return Redisson.create(config); }
獲取鎖操作:
long waitTimeout = 10; long leaseTime = 1; RLock lock1 = redissonClient1.getLock("lock1"); RLock lock2 = redissonClient2.getLock("lock2"); RLock lock3 = redissonClient3.getLock("lock3"); RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3); redLock.trylock(waitTimeout,leaseTime,TimeUnit.SECONDS); try{ //... }finally{ redLock.unlock(); }
總結(jié)
實(shí)現(xiàn)分布式鎖的方式不止這三種,最簡(jiǎn)單的就是數(shù)據(jù)庫(kù)實(shí)現(xiàn),Zookeeper實(shí)現(xiàn)也相對(duì)比較簡(jiǎn)單,但是性能最好的還是Redis實(shí)現(xiàn),但是可靠性方面,Zookeeper基于分布式集群,具有天然的優(yōu)勢(shì),可靠性相對(duì)更高。如果業(yè)務(wù)場(chǎng)景對(duì)性能要求不是很高的時(shí)候,優(yōu)先使用Zookeeper實(shí)現(xiàn)分布式鎖。
到此,相信大家對(duì)“如何使用分布式鎖”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。