乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      基于Redis實(shí)現(xiàn)分布式鎖

       頭號(hào)碼甲 2020-12-11

      我們知道分布式鎖的特性是排他、避免死鎖、高可用。分布式鎖的實(shí)現(xiàn)可以通過(guò)數(shù)據(jù)庫(kù)的樂(lè)觀鎖(通過(guò)版本號(hào))或者悲觀鎖(通過(guò)for update)、Redis的setnx()命令、Zookeeper(在某個(gè)持久節(jié)點(diǎn)添加臨時(shí)有序節(jié)點(diǎn),判斷當(dāng)前節(jié)點(diǎn)是否是序列中最小的節(jié)點(diǎn),如果不是則監(jiān)聽(tīng)比當(dāng)前節(jié)點(diǎn)還要小的節(jié)點(diǎn)。如果是,獲取鎖成功。當(dāng)被監(jiān)聽(tīng)的節(jié)點(diǎn)釋放了鎖(也就是被刪除),會(huì)通知當(dāng)前節(jié)點(diǎn)。然后當(dāng)前節(jié)點(diǎn)再?lài)L試獲取鎖,如此反復(fù))

       
      redis.png

      本篇文章,主要講如何用Redis的形式實(shí)現(xiàn)分布式鎖。后續(xù)文章會(huì)講解熱點(diǎn)KEY讀取,緩存穿透和緩存雪崩的場(chǎng)景和解決方案、緩存更新策略等等知識(shí)點(diǎn),理論知識(shí)點(diǎn)較多。

      Redis配置

      spring:
        redis:
          port: 6379
          database: 0
          host: 127.0.0.1
          password:
          jedis:
            pool:
              max-active: 8
              max-wait: -1ms
              max-idle: 8
              min-idle: 0
          timeout: 5000ms 

      Jedis工具類(lèi)

      public class JedisConnectionUtil {
      
          private static JedisPool pool = null;
      
          static {
              JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
              jedisPoolConfig.setMaxTotal(100);
              pool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379);
          }
      
          public static Jedis getJedis(){
              return pool.getResource();
          }
      }
      

        

      分布式鎖分析與編碼

      下面進(jìn)入正文。因?yàn)榉植际较到y(tǒng)之間是不同進(jìn)程的,單機(jī)版的鎖無(wú)法滿(mǎn)足要求。所以我們可以借助中間件Redis的setnx()命令實(shí)現(xiàn)分布式鎖。setnx()命令只會(huì)對(duì)不存在的key設(shè)值,返回1代表獲取鎖成功。對(duì)存在的key設(shè)值,會(huì)返回0代表獲取鎖失敗。這里的value是System.currentTimeMillis() (獲取鎖的時(shí)間)+鎖持有的時(shí)間。我這里設(shè)置鎖持有的時(shí)間是200ms,實(shí)際業(yè)務(wù)執(zhí)行的時(shí)間遠(yuǎn)比這200ms要多的多,持有鎖的客戶(hù)端應(yīng)該檢查鎖是否過(guò)期,保證鎖在釋放之前不會(huì)過(guò)期。因?yàn)榭蛻?hù)端故障的情況可能是很復(fù)雜的。比如現(xiàn)在有A,B倆個(gè)客戶(hù)端。A客戶(hù)端獲取了鎖,執(zhí)行業(yè)務(wù)中做了騷操作導(dǎo)致阻塞了很久,時(shí)間應(yīng)該遠(yuǎn)遠(yuǎn)超過(guò)200ms,當(dāng)A客戶(hù)端從阻塞狀態(tài)下恢復(fù)繼續(xù)執(zhí)行業(yè)務(wù)代碼時(shí),A客戶(hù)端持有的鎖由于過(guò)期已經(jīng)被其他客戶(hù)端占有。這時(shí)候A客戶(hù)端執(zhí)行釋放鎖的操作,那么有可能釋放掉其他客戶(hù)端的鎖。

      我這里設(shè)置的客戶(hù)端等待鎖的時(shí)間是200ms。這里通過(guò)輪詢(xún)的方式去讓客戶(hù)端獲取鎖。如果客戶(hù)端在200ms之內(nèi)沒(méi)有鎖的話(huà),直接返回false。實(shí)際場(chǎng)景要設(shè)置合適的客戶(hù)端等待鎖的時(shí)間,避免消耗CPU資源。

      獲取鎖

      /**
       * 獲取鎖
       * @param lockName 鎖的名字
       * @param acquireTimeout 或得所的超時(shí)時(shí)間
       * @param lockTimeout 所本身的超時(shí)時(shí)間
       * @return
       */
      public String acquireLock(String lockName, long acquireTimeout, long lockTimeout){
          // 鎖標(biāo)識(shí)
          String identifler = UUID.randomUUID().toString();
          String lockKey = "lock" + lockName;
          int lockExpire = (int) (lockTimeout / 1000);
          Jedis jedis = null;
      
          try {
              jedis = JedisConnectionUtil.getJedis();
      
              long end = System.currentTimeMillis() + acquireTimeout;
              // 獲取鎖的限定時(shí)間
              while (System.currentTimeMillis() < end) {
                  if (jedis.setnx(lockKey, identifler) == 1) {
                      // 設(shè)置成功
                      // 設(shè)置超時(shí)時(shí)間
                      jedis.expire(lockKey, lockExpire);
                      // 或得鎖成功
                      return identifler;
                  }
                  if (jedis.ttl(lockKey) == -1) {
                      // 如果一直沒(méi)有或得鎖設(shè)置超時(shí)時(shí)間
                      jedis.expire(lockKey, lockExpire);
                  }
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
          }finally {
              jedis.close();
          }
          return null;
      }
      

        

      所以,我們要加上鎖過(guò)期,然后獲取鎖的策略。通過(guò)realKey獲取當(dāng)前的currentValue。currentValue也就是獲取鎖的時(shí)間 + 鎖持有的時(shí)間。 如果currentValue不等于null 且 currentValue 小于當(dāng)前時(shí)間,說(shuō)明鎖已經(jīng)過(guò)期。這時(shí)候如果突然來(lái)了C,D兩個(gè)客戶(hù)端獲取鎖的請(qǐng)求,不就讓C,D兩個(gè)客戶(hù)端都獲取鎖了嗎。如果防止這種現(xiàn)象發(fā)生,我們采用getSet()命令來(lái)解決。getSet(key,value)的命令會(huì)返回key對(duì)應(yīng)的value,然后再把key原來(lái)的值更新為value。也就是說(shuō)getSet()返回的是已過(guò)期的時(shí)間戳。如果這個(gè)已過(guò)期的時(shí)間戳等于currentValue,說(shuō)明獲取鎖成功。
       

      釋放鎖

      /**
       * 釋放鎖
       * @param lockName 鎖的名字
       * @param identifler 鎖標(biāo)識(shí)
       * @return
       */
      public boolean releaseLock(String lockName, String identifler){
          System.out.println(lockName + "釋放鎖" + identifler);
          String lockKey = "lock:" + lockName;
          Jedis jedis = null;
          boolean isrelease = false;
          try {
              jedis = JedisConnectionUtil.getJedis();
              while (true){
                  jedis.watch(lockKey);
                  // 判斷是否是同一把鎖
                  if (identifler.equals(jedis.get(lockKey))){
                      Transaction transaction = jedis.multi();
                      transaction.del(lockKey);
                      if (transaction.exec().isEmpty()){
                          continue;
                      }
                      isrelease = true;
                  }
                  // TODO 異常
                  jedis.unwatch();
                  break;
              }
          }finally {
              jedis.close();
          }
          return isrelease;
      }
      

      編寫(xiě)測(cè)試類(lèi)用多線(xiàn)程模擬高并發(fā)

      public class UnitTest extends Thread {
      
          @Override
          public void run() {
              while (true){
                  DistributedLock distributedLock = new DistributedLock();
                  String rs = distributedLock.acquireLock("updateOrder", 2000, 5000);
                  if (rs != null){
                      System.out.println(Thread.currentThread().getName() + "-> 或得鎖" + rs);
                      try {
                          Thread.sleep(1000);
                          distributedLock.releaseLock("updateOrder", rs);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      break;
                  }
              }
          }
      
          public static void main(String[] args) {
              UnitTest unitTest = new UnitTest();
              for (int i = 0; i < 10; i++) {
                  new Thread(unitTest, "tName:" + i).start();
              }
          }
      }
      

      代碼運(yùn)行結(jié)果

      tName:0-> 或得鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab
      updateOrder釋放鎖a86a2867-a5f9-4b81-8be6-105d50b8a1ab
      tName:1-> 或得鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0
      updateOrder釋放鎖bcdeb19c-a47c-4a12-86df-8e5d90b2ecb0
      tName:6-> 或得鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f
      updateOrder釋放鎖147d9fc8-5e15-4e86-8da6-e029a3e2d92f
      tName:2-> 或得鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca
      updateOrder釋放鎖93deb41d-2439-45e7-beb1-2e4ce672e8ca
      tName:4-> 或得鎖094f921d-fe9b-46ba-873b-aee2ce974f16
      updateOrder釋放鎖094f921d-fe9b-46ba-873b-aee2ce974f16
      tName:5-> 或得鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9
      updateOrder釋放鎖216e0799-6d22-4ae4-bb83-9efe1e5f80c9
      tName:9-> 或得鎖678e2099-651c-4e23-a648-9fb588ecb42b
      updateOrder釋放鎖678e2099-651c-4e23-a648-9fb588ecb42b
      tName:7-> 或得鎖f35cdbad-6fde-4f1e-a4c1-321805a39374
      updateOrder釋放鎖f35cdbad-6fde-4f1e-a4c1-321805a39374
      tName:3-> 或得鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072
      updateOrder釋放鎖0913a4a0-805a-48e2-ac5a-c0762b8c4072
      tName:8-> 或得鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce
      updateOrder釋放鎖a3964a2a-6b9c-4ca3-9ea5-53de854c23ce
       
       
       
      本文文字部分參考文章:https://www.jianshu.com/p/83224c0f3bb9

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多