分散式鎖----Redis實現

堅持到底gl發表於2019-07-26

   分散式鎖

  為什麼需要有分散式鎖呢,在單點的時候synchronized 就能解決,但是服務拆分之後,每個服務都是單獨的機器,無法解決,所以出現了分散式鎖,其實也就是用各種手段,實現獲取唯一鎖,別人無法得到。

  其實在做分散式鎖的前提,需要先明白,synchronized  為啥不能使用了,啥原理讓他在一個機器上可以使用。

  


      synchronized 的原理   

      眾所周知 Synchronize 關鍵字是解決併發問題常用解決方案,有以下三種使用方式:

  • 同步靜態方法,鎖的是當前 Class 物件。
  • 同步塊,鎖的是 {} 中的物件。
  •  同步普通方法,鎖的是當前物件。

  實現原理:
  JVM 是通過進入、退出物件監視器( Monitor )來實現對方法、同步塊的同步的。

  具體實現是在編譯之後在同步方法呼叫前加入一個 monitor.enter 指令,在退出方法和異常處插入 monitor.exit 的指令。

  其本質就是對一個物件監視器( Monitor )進行獲取,而這個獲取過程具有排他性從而達到了同一時刻只能一個執行緒訪問的目的。

  而對於沒有獲取到鎖的執行緒將會阻塞到方法入口處,直到獲取鎖的執行緒 monitor.exit 之後才能嘗試繼續獲取鎖。

 

可以通過使用 javap -c Synchronize 可以檢視編譯之後的具體資訊 ,這裡我就不再貼上了。

所以可以知道,單獨的Java虛擬機器是可實現鎖的,但是多臺手就伸不到了,只能在依賴外部的形式去產生一個唯一鎖。

以上是參考別人的部落格拿到的資訊,親自試用得到,準確   :連結:https://www.jianshu.com/p/2ba154f275ea


  Redis實現的分散式鎖

  主要是利用了redis的set NX的原理,以及對redis的script指令碼原子性利用。(個人看法,其實後面一步就看各自的程式邏輯如何去判定到底要不要這一步了)

  簡單的說一下主要流程:

  1. 首先設定一個全域性唯一的key和一個唯一性的value(value是一個解鎖的保障,刪除之前判斷一下值是否一致)
  2. 使用Redis的set 方法 以多引數形式配置key,value,nx,px,過期時間 (引數 NX:只有鍵不存在時,才能對其進行設定操作)
  3. 利用Redis的script指令碼來對key的刪除操作,只能自己刪除自己的value(刪除之前先判斷一下value是否是我之前的value,是否被改過,沒有就刪了)
   這是指令碼的主要 內容 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else  return 0 end  
   對其解釋: 根據 get 獲取到 key 的值 ,判斷key的值是否跟你傳的值相等,相等則 執行del  key 否則返回0 結束
   對此推薦去看一下redis的Lua指令碼,給個簡單的:http://redisdoc.com/script/eval.html
  
  在實際場景中可以利用AOP的切點切面形式,實現具體是什麼地方需要分散式鎖,搭配分散式鎖,再搭配註解方式,來實現想要的自定義設定那裡需要就點哪裡
  AOP的主要實現是
  1. @pointcut 切註解類
  2. 在使用@around環繞對前後做處理
  3. 前面加鎖
  4. 後面解鎖del  使用script指令碼
  

  程式碼主要實現

   註解類 DistributedLock

  

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DistributedLock {

    /**
     * 自定義形式新增你對分散式鎖的對外配置屬性  
     * 例如:key的規則,鎖的超時時間,獲取鎖的等待時間,等一系列的屬性配置
     * 
     */

}
  切面類 DistributedLockAspect
@Aspect
@Component
public class DistributedLockAspect {
    
     /**
     * 層切點
     */
    @Pointcut("@annotation(com.creditease.hardess.common.annotation.DistributedLock)")
    public void distributedLockAspect() {}

/**
     * @param joinPoint 切點
     * @return Object 新增分散式鎖後,實際呼叫業務邏輯部分程式碼的返回值
     * @throws Throwable 產生的所有異常,為了避免對異常處理流程產生干擾,所有異常都應該繼續丟擲
     */
    @Around("distributedLockAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
     /**
      *主要寫一些鎖的獲取,和業務邏輯執行,鎖的刪除等
      *
     */
     return returnObject;
    }

   /**
     * 獲取 DistributedLock 註解
     * 
     * @param joinPoint
     * @return 程式碼中定義的註解
     * @throws NoSuchMethodException
     */
    private static DistributedLock getDistributedLock(ProceedingJoinPoint joinPoint)
            throws NoSuchMethodException {
        String methodName = joinPoint.getSignature().getName();

        Class<?> classTarget = joinPoint.getTarget().getClass();
        Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Method objMethod = classTarget.getMethod(methodName, par);

        return objMethod.getAnnotation(DistributedLock.class);
    }

}




}

 

相關文章