<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          注解方式優(yōu)雅的實(shí)現(xiàn) Redisson 分布式鎖

          共 14185字,需瀏覽 29分鐘

           ·

          2023-11-06 18:33

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          來(lái)源:juejin.cn/post/7215142807861379109

          1前言

          日常開發(fā)中,難免遇到一些并發(fā)的場(chǎng)景,為了保證接口執(zhí)行的一致性,通常采用加鎖的方式,因?yàn)榉?wù)是分布式部署模式,本地鎖Reentrantlock和Synchnorized這些就先放到一邊了,Redis的setnx鎖存在無(wú)法抱保證原子性的問(wèn)題就暫時(shí)擱且到一邊,直接上大招Redisson也是我最近開發(fā)項(xiàng)目中基本都在用的緩存,并且也都是用它的分布式鎖機(jī)制。

          2Redisson分布式鎖常規(guī)使用

          關(guān)于Redisson的一些基本概念,本章就不做太詳細(xì)的說(shuō)明了,有興趣的小伙伴可以自己去了解下,主要說(shuō)下加鎖的常規(guī)使用,Redisson分布式鎖是基于Redis的Rlock鎖,實(shí)現(xiàn)了JavaJUC包下的Lock接口。

          Lock

          public void getLock(){
              //獲取鎖
              RLock lock = redisson.getLock("Lxlxxx_Lock");
              try {
                  // 2.加鎖
                  lock.lock();

              } catch (InterruptedException e) {
                  e.getStackTrace();
              } finally {
                  // 3.解鎖
                  lock.unlock();
                  System.out.println("Finally,釋放鎖成功");
              }

          getLock獲取鎖,lock.lock進(jìn)行加鎖,會(huì)出現(xiàn)的問(wèn)題就是lock拿不到鎖一直等待,會(huì)進(jìn)入阻塞狀態(tài),顯然這樣是不好的。

          TryLock

          返回boolean類型,和Reentrantlock的tryLock是一個(gè)意思,嘗試獲取鎖,獲取到就返回true,獲取失敗就返回false,不會(huì)使獲不到鎖的線程一直處于等待狀態(tài),返回false可以繼續(xù)執(zhí)行下面的業(yè)務(wù)邏輯,當(dāng)然Ression鎖內(nèi)部也涉及到watchDog看門狗機(jī)制,主要作用就是給快過(guò)期的鎖進(jìn)行續(xù)期,主要用途就是使拿到鎖的有限時(shí)間讓業(yè)務(wù)執(zhí)行完,再進(jìn)行鎖釋放。

          RLock lock = redisson.getLock(name);
          try {

              if (lock.tryLock(2, 10, TimeUnit.SECONDS)) {
                  //執(zhí)行業(yè)務(wù)邏輯
              } else {
                  System.out.println("已存在");
              }
          } catch (InterruptedException e) {
              e.printStackTrace();
          }finally {
          //判斷當(dāng)前線程持有的鎖是不是處于鎖定狀態(tài),鎖定狀態(tài)再進(jìn)行釋放
              if (this.redissonLock.isHeldByCurrentThread(lockName)) {
                  this.redissonLock.unlock(lockName);
              }
          }

          3自定義注解實(shí)現(xiàn)鎖機(jī)制

          通常我們都會(huì)將redisson實(shí)例注入到方法類里面,然后調(diào)用加鎖方法進(jìn)行加鎖,如果其他業(yè)務(wù)方法也需要加鎖執(zhí)行,將會(huì)產(chǎn)生很多重復(fù)代碼,由此采用AOP切面的方式,只需要通過(guò)注解的方式就能將方法進(jìn)行加鎖處理。

          自定義注解

          @Documented
          @Inherited
          @Retention(RetentionPolicy.RUNTIME)
          @Target({ElementType.METHOD})
          public @interface DistributedLock {
              String key() default "";

              int leaseTime() default 10;

              boolean autoRelease() default true;

              String errorDesc() default "系統(tǒng)正常處理,請(qǐng)稍后提交";

              int waitTime() default 1;
          }

          切面類實(shí)現(xiàn)

          @Aspect
          @Component
          public class DistributedLockHandler {
              private static final Logger log = LoggerFactory.getLogger(DistributedLockHandler.class);
              @Autowired
              RedissonLock redissonLock;

              public DistributedLockHandler() {
              }

              @Around("@annotation(distributedLock)")
              public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
                  String lockName = this.getRedisKey(joinPoint, distributedLock);
                  int leaseTime = distributedLock.leaseTime();
                  String errorDesc = distributedLock.errorDesc();
                  int waitTime = distributedLock.waitTime();

                  Object var8;
                  try {
                      boolean lock = this.redissonLock.tryLock(lockName, (long)leaseTime, (long)waitTime);
                      if (!lock) {
                          throw new RuntimeException(errorDesc);
                      }

                      var8 = joinPoint.proceed();
                  } catch (Throwable var12) {
                      log.error("執(zhí)行業(yè)務(wù)方法異常", var12);
                      throw var12;
                  } finally {
                      if (this.redissonLock.isHeldByCurrentThread(lockName)) {
                          this.redissonLock.unlock(lockName);
                      }

                  }

                  return var8;
              }


              /**
               *  獲取加鎖的key
               * @param joinPoint
               * @param distributedLock
               * @return
               */
              private String getRedisKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
                  String key = distributedLock.key();
                  Object[] parameterValues = joinPoint.getArgs();
                  MethodSignature signature = (MethodSignature)joinPoint.getSignature();
                  Method method = signature.getMethod();
                  DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
                  String[] parameterNames = nameDiscoverer.getParameterNames(method);
                  if (StringUtils.isEmpty(key)) {
                      if (parameterNames != null && parameterNames.length > 0) {
                          StringBuffer sb = new StringBuffer();
                          int i = 0;

                          for(int len = parameterNames.length; i < len; ++i) {
                              sb.append(parameterNames[i]).append(" = ").append(parameterValues[i]);
                          }

                          key = sb.toString();
                      } else {
                          key = "redissionLock";
                      }

                      return key;
                  } else {
                      SpelExpressionParser parser = new SpelExpressionParser();
                      Expression expression = parser.parseExpression(key);
                      if (parameterNames != null && parameterNames.length != 0) {
                          EvaluationContext evaluationContext = new StandardEvaluationContext();

                          for(int i = 0; i < parameterNames.length; ++i) {
                              evaluationContext.setVariable(parameterNames[i], parameterValues[i]);
                          }

                          try {
                              Object expressionValue = expression.getValue(evaluationContext);
                              return expressionValue != null && !"".equals(expressionValue.toString()) ? expressionValue.toString() : key;
                          } catch (Exception var13) {
                              return key;
                          }
                      } else {
                          return key;
                      }
                  }
              }
          }

          具體使用

          圖片

          方法頭加自定義注解,key參數(shù)代表需要加鎖的key,errorDesc獲取鎖失敗提示報(bào)錯(cuò)信息。

          這邊我將項(xiàng)目通過(guò)修改端口啟動(dòng)了兩個(gè)服務(wù),分別是8460和8461

          圖片
          圖片

          通過(guò)postman調(diào)用這兩個(gè)服務(wù),模擬兩個(gè)服務(wù)同時(shí)獲取一把鎖的場(chǎng)景,其中一個(gè)服務(wù)拿到鎖,另外一個(gè)服務(wù)獲取鎖失敗。

          圖片

          可以看到端口8460服務(wù)先拿到鎖,8461服務(wù)tryLock獲取鎖失敗,實(shí)現(xiàn)了加鎖邏輯。

          圖片
          圖片

          4總結(jié)

          分布式鎖的使用場(chǎng)景還是需要多注意下,根據(jù)業(yè)務(wù)場(chǎng)景來(lái),并發(fā)量不大的情況下,其實(shí)沒(méi)有必要加,可能在移動(dòng)端操作比較頻繁的情況下需要注意并發(fā),目前我做的b端項(xiàng)目,通過(guò)簡(jiǎn)單接口冪等性操作就可以避免重復(fù)提交,切勿不要盲目加鎖,多少會(huì)影響一些性能。

               
               

            

                  
                  

                   
                   

          1、如何搭建一個(gè)拖垮公司的技術(shù)架構(gòu)?【文末送書】

          2、原來(lái),這才是 JDK 推薦的線程關(guān)閉方式

          3、MySQL到底是 join 性能好,還是in一下更快呢?

          4、前端同事最討厭的后端行為,看看你中了沒(méi)有

          5、相比高人氣的Rust、Go,為何 Java、C 在工具層面進(jìn)展緩慢?

          6、讓程序員早點(diǎn)下班的《技術(shù)寫作指南》

          點(diǎn)

          點(diǎn)

          點(diǎn)點(diǎn)

          點(diǎn)在看

          瀏覽 819
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产经典的三级字在线播放 | 欧美大黑逼 | 黑人日亚洲美女 | 无码免费观看视频 | 兔子先生把酒醉姐姐带回家 |