springboot整合curator實現分佈式鎖

理論篇:git

Curator是Netflix開源的一套ZooKeeper客戶端框架. Netflix在使用ZooKeeper的過程當中發現ZooKeeper自帶的客戶端太底層, 應用方在使用的時候須要本身處理不少事情, 因而在它的基礎上包裝了一下, 提供了一套更好用的客戶端框架. Netflix在用ZooKeeper的過程當中遇到的問題, 咱們也遇到了, 因此開始研究一下, 首先從他在github上的源碼, wiki文檔以及Netflix的技術blog入手. 

看完官方的文檔以後, 發現Curator主要解決了三類問題: github

  • 封裝ZooKeeper client與ZooKeeper server之間的鏈接處理;
  • 提供了一套Fluent風格的操做API;
  • 提供ZooKeeper各類應用場景(recipe, 好比共享鎖服務, 集羣領導選舉機制)的抽象封裝.



Curator列舉的ZooKeeper使用過程當中的幾個問題 
初始化鏈接的問題: 在client與server之間握手創建鏈接的過程當中, 若是握手失敗, 執行全部的同步方法(好比create, getData等)將拋出異常 
自動恢復(failover)的問題: 當client與一臺server的鏈接丟失,並試圖去鏈接另一臺server時, client將回到初始鏈接模式 
session過時的問題: 在極端狀況下, 出現ZooKeeper session過時, 客戶端須要本身去監聽該狀態並從新建立ZooKeeper實例 . 
對可恢復異常的處理:當在server端建立一個有序ZNode, 而在將節點名返回給客戶端時崩潰, 此時client端拋出可恢復的異常, 用戶須要本身捕獲這些異常並進行重試 
使用場景的問題:Zookeeper提供了一些標準的使用場景支持, 可是ZooKeeper對這些功能的使用說明文檔不多, 並且很容易用錯. 在一些極端場景下如何處理, zk並無給出詳細的文檔說明. 好比共享鎖服務, 當服務器端建立臨時順序節點成功, 可是在客戶端接收到節點名以前掛掉了, 若是不能很好的處理這種狀況, 將致使死鎖. 

Curator主要從如下幾個方面下降了zk使用的複雜性: 
重試機制:提供可插拔的重試機制, 它將給捕獲全部可恢復的異常配置一個重試策略, 而且內部也提供了幾種標準的重試策略(好比指數補償). 
鏈接狀態監控: Curator初始化以後會一直的對zk鏈接進行監聽, 一旦發現鏈接狀態發生變化, 將做出相應的處理. 
zk客戶端實例管理:Curator對zk客戶端到server集羣鏈接進行管理. 並在須要的狀況, 重建zk實例, 保證與zk集羣的可靠鏈接 
各類使用場景支持:Curator實現zk支持的大部分使用場景支持(甚至包括zk自身不支持的場景), 這些實現都遵循了zk的最佳實踐, 並考慮了各類極端狀況. 

Curator經過以上的處理, 讓用戶專一於自身的業務自己, 而無需花費更多的精力在zk自己. spring

 實操篇:安全

CuratorFrameworkFactory類提供了兩個方法, 一個工廠方法newClient, 一個構建方法build. 使用工廠方法newClient能夠建立一個默認的實例, 而build構建方法能夠對實例進行定製. 當CuratorFramework實例構建完成, 緊接着調用start()方法, 在應用結束的時候, 須要調用close()方法.  CuratorFramework是線程安全的. 在一個應用中能夠共享同一個zk集羣的CuratorFramework. 服務器

核心對象CuratorFramework的建立以下:session

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();

 

分佈式鎖經常使用於定時任務,使用自定義註解,使用spring aspect around, 在真正的代碼執行以前嘗試獲取鎖,獲取不到直接退出,獲取到鎖的,執行具體業務,代碼以下:分佈式

@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();
   }
 }
} 

自定義註解:ui

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface DistributedLock{
4    String lockPath();  
5 }

 

注意事項:spa

   1.  CuratorFramework對象建議在應用中作單例處理,在具體使用處 注入使用, 並在應用結束前銷燬,代碼以下:

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

2. 在aspect部分將curatorFramework對象進行關閉

@PreDestroy
public void destroy(){
   CloseableUtils.closeQuietly(curatorFramework);
}
相關文章
相關標籤/搜索