欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

springboot整合curator實(shí)現(xiàn)分布式鎖過程

 更新時間:2022年05月07日 10:25:06   作者:西鳳樓  
這篇文章主要介紹了springboot整合curator實(shí)現(xiàn)分布式鎖過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

springboot curator實(shí)現(xiàn)分布式鎖

理論篇:

Curator是Netflix開源的一套ZooKeeper客戶端框架. Netflix在使用ZooKeeper的過程中發(fā)現(xiàn)ZooKeeper自帶的客戶端太底層, 應(yīng)用方在使用的時候需要自己處理很多事情, 于是在它的基礎(chǔ)上包裝了一下, 提供了一套更好用的客戶端框架. Netflix在用ZooKeeper的過程中遇到的問題, 我們也遇到了, 所以開始研究一下, 首先從他在github上的源碼, wiki文檔以及Netflix的技術(shù)blog入手. 

看完官方的文檔之后, 發(fā)現(xiàn)Curator主要解決了三類問題:

  • 封裝ZooKeeper client與ZooKeeper server之間的連接處理;
  • 提供了一套Fluent風(fēng)格的操作API;
  • 提供ZooKeeper各種應(yīng)用場景(recipe, 比如共享鎖服務(wù), 集群領(lǐng)導(dǎo)選舉機(jī)制)的抽象封裝.

Curator列舉的ZooKeeper使用過程中的幾個問題 

  • 初始化連接的問題: 在client與server之間握手建立連接的過程中, 如果握手失敗, 執(zhí)行所有的同步方法(比如create, getData等)將拋出異常 
  • 自動恢復(fù)(failover)的問題: 當(dāng)client與一臺server的連接丟失,并試圖去連接另外一臺server時, client將回到初始連接模式 
  • session過期的問題: 在極端情況下, 出現(xiàn)ZooKeeper session過期, 客戶端需要自己去監(jiān)聽該狀態(tài)并重新創(chuàng)建ZooKeeper實(shí)例 . 
  • 對可恢復(fù)異常的處理:當(dāng)在server端創(chuàng)建一個有序ZNode, 而在將節(jié)點(diǎn)名返回給客戶端時崩潰, 此時client端拋出可恢復(fù)的異常, 用戶需要自己捕獲這些異常并進(jìn)行重試 
  • 使用場景的問題:Zookeeper提供了一些標(biāo)準(zhǔn)的使用場景支持, 但是ZooKeeper對這些功能的使用說明文檔很少, 而且很容易用錯. 在一些極端場景下如何處理, zk并沒有給出詳細(xì)的文檔說明. 比如共享鎖服務(wù), 當(dāng)服務(wù)器端創(chuàng)建臨時順序節(jié)點(diǎn)成功, 但是在客戶端接收到節(jié)點(diǎn)名之前掛掉了, 如果不能很好的處理這種情況, 將導(dǎo)致死鎖. 

Curator主要從以下幾個方面降低了zk使用的復(fù)雜性: 

  • 重試機(jī)制:提供可插拔的重試機(jī)制, 它將給捕獲所有可恢復(fù)的異常配置一個重試策略, 并且內(nèi)部也提供了幾種標(biāo)準(zhǔn)的重試策略(比如指數(shù)補(bǔ)償). 
  • 連接狀態(tài)監(jiān)控: Curator初始化之后會一直的對zk連接進(jìn)行監(jiān)聽, 一旦發(fā)現(xiàn)連接狀態(tài)發(fā)生變化, 將作出相應(yīng)的處理. 
  • zk客戶端實(shí)例管理:Curator對zk客戶端到server集群連接進(jìn)行管理. 并在需要的情況, 重建zk實(shí)例, 保證與zk集群的可靠連接 
  • 各種使用場景支持:Curator實(shí)現(xiàn)zk支持的大部分使用場景支持(甚至包括zk自身不支持的場景), 這些實(shí)現(xiàn)都遵循了zk的最佳實(shí)踐, 并考慮了各種極端情況. 

Curator通過以上的處理, 讓用戶專注于自身的業(yè)務(wù)本身, 而無需花費(fèi)更多的精力在zk本身. 

實(shí)操篇:

CuratorFrameworkFactory類提供了兩個方法, 一個工廠方法newClient, 一個構(gòu)建方法build. 使用工廠方法newClient可以創(chuàng)建一個默認(rèn)的實(shí)例, 而build構(gòu)建方法可以對實(shí)例進(jìn)行定制. 當(dāng)CuratorFramework實(shí)例構(gòu)建完成, 緊接著調(diào)用start()方法, 在應(yīng)用結(jié)束的時候, 需要調(diào)用close()方法.  CuratorFramework是線程安全的. 在一個應(yīng)用中可以共享同一個zk集群的CuratorFramework. 

核心對象CuratorFramework的創(chuàng)建如下:

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.builder()
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .connectString("")
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .sessionTimeoutMs(5000)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .connectionTimeoutMs(5000)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .retryPolicy(retryPolicy)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .build();
client.start();

需要使用分布式鎖的地方,代碼如下:

String lockOn= "test";
InterProcessMutex mutex = new InterProcessMutex(curatorFramework,lockOn);
boolean locked =mutex.acquire(0,TimeUnit.SECONDS);
//finally部分 ?
mutex.release();

分布式鎖常用于定時任務(wù),使用自定義注解,使用spring aspect around, 在真正的代碼執(zhí)行之前嘗試獲取鎖,獲取不到直接退出,獲取到鎖的,執(zhí)行具體業(yè)務(wù),代碼如下:

@Aspect
public class DistributedLockAspect{
? ? @Pointcut("@annotation(com.**.**.DistributedLock")
? ? public void methodAspect(){}; ?
? ??
? ? @Around("methodAspect()")
? ? public Object execute(ProceedingJoinPoint joinPoint) throws Exception{
? ??
? ? String lockPath = "/opt/zookeeper/lock";
? ? InterProcessMutex mutex = new InterProcessMutex(cruatorFramework,lockPath);
? ? try{
? ? ? ?boolean locked = mutex.acquire(0,TimeUnit.SECONDS);
? ? ? ?if(!locked){
? ? ? ? ? return null;
? ? ? }else{
? ? ? ? return joinPoint.proceed();
? ? ? }
? ?}catch(Exception e){
? ? ? ?e.printStackTrace();
? ?}finally{
? ? ? ?mutex.release();
? ?}
?}
}?

自定義注解:

?@Target(ElementType.METHOD)
?@Retention(RetentionPolicy.RUNTIME)
?public @interface DistributedLock{
? ? String lockPath(); ?
?}

注意事項(xiàng):

1.CuratorFramework對象建議在應(yīng)用中做單例處理,在具體使用處 注入使用, 并在應(yīng)用結(jié)束前銷毀,代碼如下:

@Configration
public class CuratorConfigration{
? ? @Bean ? ?
? ? public CuratorFramework initCuratorFramework(){
? ? ? ? //忽略?
? ? ? ?// 參照前面 CuratorFramework 對象創(chuàng)建部分
? ? } ? ?
}

2.在aspect部分將curatorFramework對象進(jìn)行關(guān)閉

@PreDestroy
public void destroy(){
? ?CloseableUtils.closeQuietly(curatorFramework);
}

項(xiàng)目實(shí)際應(yīng)用中分布式鎖介紹

鎖的介紹

1、悲觀鎖

顧名思義,很悲觀,就是每次拿數(shù)據(jù)的時候都認(rèn)為別的線程會修改數(shù)據(jù),所以在每次拿的時候都會給數(shù)據(jù)上鎖。上鎖之后,當(dāng)別的線程想要拿數(shù)據(jù)時,就會阻塞,直到給數(shù)據(jù)上鎖的線程將事務(wù)提交或者回滾。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里就用到了很多這種鎖機(jī)制,比如行鎖,表鎖,共享鎖,排他鎖等,都是在做操作之前先上鎖。

2、行鎖

通過select for update語句給sid = 1的數(shù)據(jù)行上了鎖

3、表鎖

select * from student for update;

4、頁鎖

行鎖鎖指定行,表鎖鎖整張表,頁鎖是折中實(shí)現(xiàn),即一次鎖定相鄰的一組記錄。

5、共享鎖

共享鎖又稱為讀鎖,一個線程給數(shù)據(jù)加上共享鎖后,其他線程只能讀數(shù)據(jù),不能修改。

6、排他鎖

排他鎖又稱為寫鎖,和共享鎖的區(qū)別在于,其他線程既不能讀也不能修改。

7、樂觀鎖

樂觀鎖其實(shí)不會上鎖。顧名思義,很樂觀,它默認(rèn)別的線程不會修改數(shù)據(jù),所以不會上鎖。只是在更新前去判斷別的線程在此期間有沒有修改數(shù)據(jù),如果修改了,會交給業(yè)務(wù)層去處理。

  • 目前幾乎很多大型網(wǎng)站及應(yīng)用都是分布式部署的,分布式場景中的數(shù)據(jù)一致性問題一直是一個比較重要的話題。分布式的CAP理論告訴我們“任何一個分布式系統(tǒng)都無法同時滿足一致性(Consistency)、可用性(Availability)和分區(qū)容錯性(Partition tolerance),最多只能同時滿足兩項(xiàng)。”所以,很多系統(tǒng)在設(shè)計(jì)之初就要對這三者做出取舍。在互聯(lián)網(wǎng)領(lǐng)域的絕大多數(shù)的場景中,都需要犧牲強(qiáng)一致性來換取系統(tǒng)的高可用性,系統(tǒng)往往只需要保證“最終一致性”,只要這個最終時間是在用戶可以接受的范圍內(nèi)即可。
  • 在很多場景中,我們?yōu)榱吮WC數(shù)據(jù)的最終一致性,需要很多的技術(shù)方案來支持,比如分布式事務(wù)、分布式鎖等。有的時候,我們需要保證一個方法在同一時間內(nèi)只能被同一個線程執(zhí)行。在單機(jī)環(huán)境中,Java中其實(shí)提供了很多并發(fā)處理相關(guān)的API,但是這些API在分布式場景中就無能為力了。也就是說單純的Java Api并不能提供分布式鎖的能力。所以針對分布式鎖的實(shí)現(xiàn)目前有多種方案:

1、基于數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖

2、基于緩存(redis,memcached)實(shí)現(xiàn)分布式鎖

3、基于Zookeeper實(shí)現(xiàn)分布式鎖

4、在分析這幾種實(shí)現(xiàn)方案之前我們先來想一下,我們需要的分布式鎖應(yīng)該是怎么樣的?(這里以方法鎖為例,資源鎖同理)

可以保證在分布式部署的應(yīng)用集群中,同一個方法在同一時間只能被一臺機(jī)器上的一個線程執(zhí)行。

  • 這把鎖要是一把可重入鎖(避免死鎖)
  • 這把鎖最好是一把阻塞鎖(根據(jù)業(yè)務(wù)需求考慮要不要這條)
  • 有高可用的獲取鎖和釋放鎖功能
  • 獲取鎖和釋放鎖的性能要好

悲觀鎖-數(shù)據(jù)庫鎖

借助數(shù)據(jù)中自帶的鎖來實(shí)現(xiàn)分布式的鎖

public boolean lock(){
? ? connection.setAutoCommit(false)
? ? while(true){
? ? ? ? try{
? ? ? ? ? ? result = select * from methodLock where method_name=xxx for update;
? ? ? ? ? ? if(result==null){
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }catch(Exception e){
?
? ? ? ? }
? ? ? ? sleep(1000);
? ? }
? ? return false;
}

在查詢語句后面增加for update,數(shù)據(jù)庫會在查詢過程中給數(shù)據(jù)庫表增加排他鎖。當(dāng)某條記錄被加上排他鎖之后,其他線程無法再在該行記錄上增加排他鎖。

我們可以認(rèn)為獲得排它鎖的線程即可獲得分布式鎖,當(dāng)獲取到鎖之后,可以執(zhí)行方法的業(yè)務(wù)邏輯,執(zhí)行完方法之后,再通過以下方法解鎖:

public void unlock(){
? ? connection.commit();
}

通過connection.commit()操作來釋放鎖。

這種方法可以有效的解決上面提到的無法釋放鎖和阻塞鎖的問題。

阻塞鎖,for update語句會在執(zhí)行成功后立即返回,在執(zhí)行失敗時一直處于阻塞狀態(tài),直到成功。

鎖定之后服務(wù)宕機(jī),無法釋放,使用這種方式,服務(wù)宕機(jī)之后數(shù)據(jù)庫會自己把鎖釋放掉。

但是還是無法直接解決數(shù)據(jù)庫單點(diǎn)和可重入問題。

悲觀鎖-緩存鎖

相比較于基于數(shù)據(jù)庫實(shí)現(xiàn)分布式鎖的方案來說,基于緩存來實(shí)現(xiàn)在性能方面會表現(xiàn)的更好一點(diǎn)。而且很多緩存是可以集群部署的,可以解決單點(diǎn)問題。

redis2.6之后,SET命令支持超時和key存在檢查,這是一個原子操作

緩存鎖優(yōu)勢是性能出色,劣勢就是由于數(shù)據(jù)在內(nèi)存中,一旦緩存服務(wù)宕機(jī),鎖數(shù)據(jù)就丟失了。像redis自帶復(fù)制功能,可以對數(shù)據(jù)可靠性有一定的保證,但是由于復(fù)制也是異步完成的,因此依然可能出現(xiàn)master節(jié)點(diǎn)寫入鎖數(shù)據(jù)而未同步到slave節(jié)點(diǎn)的時候宕機(jī),鎖數(shù)據(jù)丟失問題。

分布式鎖—zookeeper

基于zookeeper臨時有序節(jié)點(diǎn)可以實(shí)現(xiàn)的分布式鎖。大致思想即為:每個客戶端對某個方法加鎖時,在zookeeper上的與該方法對應(yīng)的指定節(jié)點(diǎn)的目錄下,生成一個唯一的瞬時有序節(jié)點(diǎn)。 判斷是否獲取鎖的方式很簡單,只需要判斷有序節(jié)點(diǎn)中序號最小的一個。 當(dāng)釋放鎖的時候,只需將這個瞬時節(jié)點(diǎn)刪除即可。同時,其可以避免服務(wù)宕機(jī)導(dǎo)致的鎖無法釋放,而產(chǎn)生的死鎖問題。

來看下Zookeeper能不能解決前面提到的問題。

  • 鎖無法釋放:使用Zookeeper可以有效的解決鎖無法釋放的問題,因?yàn)樵趧?chuàng)建鎖的時候,客戶端會在ZK中創(chuàng)建一個臨時節(jié)點(diǎn),一旦客戶端獲取到鎖之后突然掛掉(Session連接斷開),那么這個臨時節(jié)點(diǎn)就會自動刪除掉。其他客戶端就可以再次獲得鎖。
  • 非阻塞鎖:使用Zookeeper可以實(shí)現(xiàn)阻塞的鎖,客戶端可以通過在ZK中創(chuàng)建順序節(jié)點(diǎn),并且在節(jié)點(diǎn)上綁定監(jiān)聽器,一旦節(jié)點(diǎn)有變化,Zookeeper會通知客戶端,客戶端可以檢查自己創(chuàng)建的節(jié)點(diǎn)是不是當(dāng)前所有節(jié)點(diǎn)中序號最小的,如果是,那么自己就獲取到鎖,便可以執(zhí)行業(yè)務(wù)邏輯了。
  • 不可重入:使用Zookeeper也可以有效的解決不可重入的問題,客戶端在創(chuàng)建節(jié)點(diǎn)的時候,把當(dāng)前客戶端的主機(jī)信息和線程信息直接寫入到節(jié)點(diǎn)中,下次想要獲取鎖的時候和當(dāng)前最小的節(jié)點(diǎn)中的數(shù)據(jù)比對一下就可以了。如果和自己的信息一樣,那么自己直接獲取到鎖,如果不一樣就再創(chuàng)建一個臨時的順序節(jié)點(diǎn),參與排隊(duì)。
  • 單點(diǎn)問題:使用Zookeeper可以有效的解決單點(diǎn)問題,ZK是集群部署的,只要集群中有半數(shù)以上的機(jī)器存活,就可以對外提供服務(wù)。

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。 

相關(guān)文章

最新評論