java B2B2C 源碼 多級分銷Springboot多租戶電子商城系統-springcloud項目redis分佈式鎖

在springcloud項目開發中redis分佈式鎖使用主要有兩個場景

需要JAVA Spring Cloud大型企業分佈式微服務雲構建的B2B2C電子商務平臺源碼請加企鵝求求 :二一四七七七五六三三

1.訂單重複提交或支付提交等,防止刷單
2.對某個業務進行鎖定,例如:當用戶同一時間,進行對賬戶充值和提現操作,那麼這裏需要根據用戶ID對賬戶進行鎖定,只有一個完成了纔可以進行第二個。
開發實現方式
1.pom.xml中引入jar包,最好引入到基礎模塊中,其他模塊通用

<!-- redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

創建redis操作類RedisGlobalLock(自定義)
redis提供RedisTemplate方法
redis提供三個方法:
(1)lock 獲取鎖並鎖定 本方法是立即獲取鎖狀態,如果獲取成功並鎖定,如果獲取失敗
(2)tryLock 嘗試獲取鎖並鎖定 本方式是在指定時間嘗試獲取鎖
(3)unlock 釋放鎖 當業務處理完畢必須釋放鎖
重點:
lock和tryLock區別:lock是實時獲取,tryLock是嘗試在一段時間內一直在獲取

@Service
public class RedisGlobalLock {
   private static Log log = LogFactory.getLog(RedisGlobalLock.class);
   private static final String TYPE_NAME = RedisGlobalLock.class.getTypeName();

   /** 默認30ms嘗試一次 */
   private final static long LOCK_TRY_INTERVAL    = 30L;
   /** 默認嘗試20s */
   private final static long LOCK_TRY_TIMEOUT     = 20 * 1000L;
   /** 單個業務持有鎖的時間30s,防止死鎖 */
   private final static long LOCK_EXPIRE     = 30 * 1000L;

   @Autowired
   private RedisTemplate<String, Object> redisTemplate;

   /**
    * 獲取鎖
    * @param key        鎖Key
    * @return          是否獲取鎖
    */
   public boolean lock(String key) {
      return getLock(key, 0, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
   }

   /**
    * 獲取鎖
    * @param key        鎖Key
    * @param expire      有效期
    * @param expireUnit   有效期時間單位
    * @return          是否獲取鎖
    */
   public boolean lock(String key, long expire, TimeUnit expireUnit) {
      return getLock(key, 0, expire, expireUnit);
   }

   /**
    * 嘗試獲取鎖
    * @param key     鎖Key
    * @return       是否獲取鎖
    */
   public boolean tryLock(String key) {
      return tryLock(key, LOCK_TRY_TIMEOUT, TimeUnit.MILLISECONDS);
   }

   /**
    * 嘗試獲取鎖
    * @param key     鎖Key
    * @param timeout  等待超時時間
    * @param unit    等待超時時間單位
    * @return       是否獲取鎖
    */
   public boolean tryLock(String key, long timeout, TimeUnit unit) {
      // 超時時間轉成毫秒
      timeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
      return getLock(key,timeout, LOCK_EXPIRE, TimeUnit.MILLISECONDS);
   }

   /**
    * 嘗試獲取鎖
    * @param key        鎖Key
    * @param timeout     等待超時時間
    * @param timeoutUnit  等待超時時間單位
    * @param expire      有效期
    * @param expireUnit   有效期時間單位
    * @return
    */
   public boolean tryLock(String key, long timeout, TimeUnit timeoutUnit, long expire, TimeUnit expireUnit) {
      // 超時時間轉成毫秒
      timeout = TimeUnit.MILLISECONDS.convert(timeout, timeoutUnit);
      return getLock(key,timeout, expire, expireUnit);
   }

   /**
    * 釋放鎖
    * @param key  鎖Key
    */
   public void unlock(String key) {
      key = getPrefix(TYPE_NAME) + key;
      Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
      if(null != oldExpireTime && oldExpireTime >= System.currentTimeMillis()) {
         // 大於過期時間,則刪除key
         redisTemplate.delete(key);
      }
   }

   /**
    * 獲取鎖
    * @param key     鎖鍵值
    * @param timeout  超時時間
    * @param time    全局鎖生命週期
    * @param unit    時間單位
    * @return       是否獲取到鎖
    */
   private boolean getLock(String key, long timeout, long time, TimeUnit unit) {
      key = getPrefix(TYPE_NAME) + key;
      try {
         long startTimeMillis = System.currentTimeMillis();
         do {
            long newValue = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
            Boolean isOk = redisTemplate.opsForValue().setIfAbsent(key, newValue);
            if(isOk) {
               // 獲得鎖
               redisTemplate.expire(key, time, unit);
               return true;
            }
            // 獲取過期時間
            Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key);
            if(null == oldExpireTime) {
                    oldExpireTime = 0L;
                }
            if(oldExpireTime >= System.currentTimeMillis()) {
               // 不小於系統時間並且過了超時時間,則不獲取鎖
               if((System.currentTimeMillis() - startTimeMillis) > timeout) {
                  return false;
               }

               // 休眠
               Thread.sleep(LOCK_TRY_INTERVAL);
            }
            // 新的過期時間
            long newExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit);
            Long currentExpireTime = (Long) redisTemplate.opsForValue().getAndSet(key, newExpireTime);
                if(null == currentExpireTime) {
                    currentExpireTime = 0L;
                }
            if(currentExpireTime.equals(oldExpireTime)) {
               // 獲取到鎖
               redisTemplate.expire(key, time, unit);
               return true;
            }
         } while (true);
      } catch (Exception e) {
         return false;
      }
   }

   /**
    * 獲取緩存標識前綴
    * @param typeName 類名
    * @return 前綴
    */
   protected final String getPrefix(String typeName) {
      return typeName;
   }

}

在業務邏輯層引入redis操作類

@Resource
private RedisGlobalLock redisGlobalLock;
// 1、獲取分佈式鎖防止重複調用 =====================================================
String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId;
if(redisGlobalLock.lock(key)) {
    try{
        System.out.println("--處理業務---");
    }catch (Exception e){
        throw e;
    }finally {
        // 4、釋放分佈式鎖 ================================================================
        redisGlobalLock.unlock(key);
    }
}else{
    // 如果沒有獲取鎖
    Ensure.that(true).isTrue("17000706");
}

所有鎖業務必須釋放鎖,防止死鎖

Java B2B2C多用戶商城 springcloud架構

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章