<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      學(xué)習(xí)高可靠Redis分布式鎖實(shí)現(xiàn)思路

      一、分布式鎖的必要性

      在單體應(yīng)用時(shí)代,我們使用ReentrantLocksynchronized就能解決線程安全問題。但當(dāng)系統(tǒng)拆分為分布式架構(gòu)后(目前大多數(shù)公司應(yīng)該不會(huì)只是單體應(yīng)用了),跨進(jìn)程的共享資源競(jìng)爭(zhēng)就成了必須要解決的問題。

      分布式鎖由此應(yīng)運(yùn)而生,但是必須解決三大核心問題:

      1. 競(jìng)態(tài)條件:多人操作共享資源,順序不可控
      2. 鎖失效:鎖自動(dòng)過期但業(yè)務(wù)未執(zhí)行完,其他客戶端搶占資源 / 加鎖成功但未設(shè)置過期時(shí)間,服務(wù)宕機(jī)導(dǎo)致死鎖
      3. 鎖誤刪:客戶端A釋放了客戶端B持有的鎖。

      二、核心實(shí)現(xiàn)解析(附源碼)

      2.1 原子性加鎖

      local lockKey = KEYS[1]              -- 鎖的鍵名,如"order_lock_123"
      local lockSecret = ARGV[1]           -- 鎖的唯一標(biāo)識(shí)(建議UUID)
      local expireTime = tonumber(ARGV[2]) -- 過期時(shí)間(單位:秒)
      
      -- 參數(shù)有效性校驗(yàn)
      if not expireTime or expireTime <= 0 then
          return "0" -- 參數(shù)非法直接返回失敗
      end
      
      -- 原子操作:SET lockKey lockSecret NX EX expireTime
      local result = redis.call("set", lockKey, lockSecret, "NX", "EX", expireTime)
      return result and "1" or "0" -- 成功返回"1",失敗返回"0"
      

      設(shè)計(jì)思路:

      • lockSecret 使用客戶端唯一標(biāo)識(shí)(推薦SnowflakeID)
      • 參數(shù)校驗(yàn):防止傳入非法過期時(shí)間
      • 原子性:?jiǎn)蚊钔瓿?判斷+設(shè)置+過期"操作

      2.2 看門狗續(xù)期機(jī)制

      local lockKey = KEYS[1]              -- 鎖的鍵名
      local lockSecret = ARGV[1]           -- 鎖標(biāo)識(shí)
      local expireTime = tonumber(ARGV[2]) -- 新的過期時(shí)間
      
      -- 參數(shù)校驗(yàn)
      if not expireTime or expireTime <= 0 then
          return "0"
      end
      
      -- 獲取當(dāng)前鎖的值
      local storedSecret = redis.call("get", lockKey)
      
      -- 續(xù)期邏輯
      if storedSecret == lockSecret then
          -- 值匹配則延長(zhǎng)過期時(shí)間
          local result = redis.call("expire", lockKey, expireTime)
          return result == 1 and "1" or "0" -- 續(xù)期成功返回"1"
      else
          -- 鎖不存在或值不匹配
          return "0"
      end
      
      // 定時(shí)續(xù)約線程
      watchdogExecutor.scheduleAtFixedRate(() -> {
          locks.entrySet().removeIf(entry -> entry.getValue().isCancelled());
          for (Entry<String, Lock> entry : locks.entrySet()) {
              if (!entry.getValue().isCancelled()) {
                  String result = redisTemplate.execute(RENEWAL_SCRIPT, 
                      Collections.singletonList(key), 
                      lock.value, "30");
                  if ("0".equals(result)) lock.cancel();
              }
          }
      }, 0, 10, TimeUnit.SECONDS);
      

      設(shè)計(jì)思路:

      • 續(xù)期間隔=過期時(shí)間/3(如30s過期則10s續(xù)期)
      • 異步線程池需單獨(dú)配置
      • 雙重校驗(yàn)鎖狀態(tài)(內(nèi)存標(biāo)記+Redis實(shí)際值)

      2.3 安全釋放鎖

      local lockKey = KEYS[1]       -- 鎖的鍵名
      local lockSecret = ARGV[1]    -- 要釋放的鎖標(biāo)識(shí)
      
      -- 獲取當(dāng)前鎖的值
      local storedSecret = redis.call("get", lockKey)
      
      -- 校驗(yàn)鎖歸屬
      if storedSecret == lockSecret then
          -- 值匹配則刪除Key
          return redis.call("del", lockKey) == 1 and "1" or "0"
      else
          -- 值不匹配
          return "0" 
      end
      

      設(shè)計(jì)思路:

      • 校驗(yàn)value避免誤刪其他線程的鎖

      三、源碼

      package org.example.tao.util;
      
      import com.alibaba.fastjson2.JSON;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.core.script.RedisScript;
      
      import javax.annotation.PreDestroy;
      import java.util.Collections;
      import java.util.Map;
      import java.util.Objects;
      import java.util.concurrent.ConcurrentHashMap;
      import java.util.concurrent.Executors;
      import java.util.concurrent.ScheduledExecutorService;
      import java.util.concurrent.TimeUnit;
      
      public class RedisUtils {
      
          static class Lock {
              private final String value;
              private volatile boolean isCancelled = false;
      
              public Lock(String value) {
                  this.value = value;
              }
      
              public boolean isCancelled() {
                  return isCancelled;
              }
      
              public void cancel() {
                  isCancelled = true;
              }
          }
      
          private static final String LOCK_LUA = "local lockKey = KEYS[1]\n" + "local lockSecret = ARGV[1]\n" + "local expireTime = tonumber(ARGV[2])  -- 動(dòng)態(tài)過期時(shí)間\n" + "if not expireTime or expireTime <= 0 then\n" + "    return \"0\"\n" + "end\n" + "local result = redis.call(\"set\", lockKey, lockSecret, \"NX\", \"EX\", expireTime)\n" + "return result and \"1\" or \"0\"";
          private static final String RELEASE_LOCK_LUA = "local lockKey = KEYS[1]\n" + "local lockSecret = ARGV[1]\n" + "local storedSecret = redis.call(\"get\", lockKey)\n" + "if storedSecret == lockSecret then\n" + "    return redis.call(\"del\", lockKey) == 1 and \"1\" or \"0\"\n" + "else\n" + "    return \"0\"\n" + "end";
          private static final String RENEWAL_LUA = "local lockKey = KEYS[1]\n" + "local lockSecret = ARGV[1]\n" + "local expireTime = tonumber(ARGV[2])\n" + "if not expireTime or expireTime <= 0 then\n" + "    return \"0\"\n" + "end\n" + "local storedSecret = redis.call(\"get\", lockKey)\n" + "if storedSecret == lockSecret then\n" + "    local result = redis.call(\"expire\", lockKey, expireTime)\n" + "    return result == 1 and \"1\" or \"0\"\n" + "else\n" + "    return \"0\"\n" + "end";
      
          private final String defaultExpireTime = "30";
          private final RedisTemplate<String, String> redisTemplate;
          private final Map<String, Lock> locks = new ConcurrentHashMap<>();
          private final ScheduledExecutorService watchdogExecutor = Executors.newScheduledThreadPool(1);
      
          public RedisUtils(RedisTemplate<String, String> redisTemplate) {
              this.redisTemplate = redisTemplate;
              watchdogExecutor.scheduleAtFixedRate(() -> {
                  try {
                      System.out.println("watchdogExecutor 執(zhí)行中... locks => " + JSON.toJSONString(locks));
                      locks.entrySet().removeIf(entry -> entry.getValue().isCancelled());
                      for (Map.Entry<String, Lock> entry : locks.entrySet()) {
                          String key = entry.getKey();
                          Lock lock = entry.getValue();
                          if (!lock.isCancelled()) {
                              RedisScript<String> redisScript = RedisScript.of(RENEWAL_LUA, String.class);
                              String result = redisTemplate.execute(redisScript, Collections.singletonList(key), lock.value, defaultExpireTime);
                              if (Objects.equals(result, "0")) {
                                  lock.cancel(); // 移除已經(jīng)釋放的鎖
                              }
                          }
                      }
                  } catch (Exception e) {
                      System.err.println("看門狗任務(wù)執(zhí)行失敗: " + e.getMessage());
                  }
              }, 0, 10, TimeUnit.SECONDS);
          }
      
          public boolean acquireLock(String key, String value) {
              RedisScript<String> redisScript = RedisScript.of(LOCK_LUA, String.class);
              String result = redisTemplate.execute(redisScript, Collections.singletonList(key), value, defaultExpireTime);
              if (Objects.equals(result, "1")) {
                  locks.put(key, new Lock(value));
                  return true;
              }
              return false;
          }
      
          public boolean acquireLockWithRetry(String key, String value, int maxRetries, long retryIntervalMillis) {
              int retryCount = 0;
              while (retryCount < maxRetries) {
                  boolean result = this.acquireLock(key, value);
                  if (result) {
                      locks.put(key, new Lock(value));
                      return true;
                  }
                  retryCount++;
                  try {
                      Thread.sleep(retryIntervalMillis);
                  } catch (InterruptedException e) {
                      Thread.currentThread().interrupt();
                      return false;
                  }
              }
              return false;
          }
      
          public boolean releaseLock(String key, String value) {
              RedisScript<String> redisScript = RedisScript.of(RELEASE_LOCK_LUA, String.class);
              String result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
              if (Objects.equals(result, "1")) {
                  Lock lock = locks.get(key);
                  if (lock != null) {
                      lock.cancel();
                  }
                  return true;
              }
              return false;
          }
      
          @PreDestroy
          public void shutdown() {
              watchdogExecutor.shutdown();
              try {
                  if (!watchdogExecutor.awaitTermination(60, TimeUnit.SECONDS)) {
                      watchdogExecutor.shutdownNow();
                  }
              } catch (InterruptedException e) {
                  watchdogExecutor.shutdownNow();
              }
          }
      }
      
      

      四、如何使用

      4.1 配置類

      @Configuration
      public class AppConfig {
      
          @Resource
          private RedisTemplate<String, String> redisTemplate;
      
          @Bean
          public RedisUtils init() {
              return new RedisUtils(redisTemplate);
          }
      
      }
      

      4.2 使用

      @RestController
      @RequestMapping("/users")
      public class UserController {
          @Resource
          private RedisUtils redisUtils;
      
      
          @PostMapping("/test2")
          public Boolean test2(@RequestBody Map<String, String> map) {
              boolean res;
              if (Objects.equals(map.get("lockFlag"), "true")) {
                  res = redisUtils.acquireLock(map.get("key"), map.get("value"));
              } else {
                  res = redisUtils.releaseLock(map.get("key"), map.get("value"));
              }
              return res;
          }
      
      }
      

      后記

      還是免責(zé)聲明,僅供學(xué)習(xí)參考

      posted @ 2025-03-13 21:55  帥氣的濤啊  閱讀(485)  評(píng)論(1)    收藏  舉報(bào)
      主站蜘蛛池模板: 久久综合色一综合色88欧美| 特黄三级又爽又粗又大| 中文字幕亚洲中文字幕无码码| 欧洲码亚洲码的区别入口| 久久一日本道色综合久久| 92自拍视频爽啪在线观看| 国产精品一区二区久久毛片| 中国农村真卖bbwbbw| 在线高清免费不卡全码| 国产亚洲一区二区三区啪| 国产无套内射普通话对白| av亚洲在线一区二区| 国产对白老熟女正在播放| 亚洲精品一区二区三区色| 国产成AV人片久青草影院| 国产清纯在线一区二区| 国产精品一区二区国产馆| 成人午夜视频在线| 2019国产精品青青草原| 樱花影院电视剧免费| 亚洲岛国成人免费av| 农村老熟妇乱子伦视频| 久热爱精品视频线路一| 国产真实乱对白精彩久久| 国产精品任我爽爆在线播放6080 | 好男人社区影视在线WWW| 国产无遮挡吃胸膜奶免费看| 久久这里只精品国产2| 精品国产一区二区色老头| 人妻少妇精品视频专区| 国内外成人综合免费视频| 亚洲色大成网站WWW久久| 久久久亚洲欧洲日产国码农村| 国产高清亚洲一区亚洲二区| 久久久精品94久久精品| 国产综合色在线精品| 67194熟妇在线观看线路| 中文字幕乱码熟妇五十中出| 铜山县| 国产又色又爽又黄的网站免费| 最新国内精品自在自线视频|