101浏览

在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架构
 

编辑器已禁用,请先登录后再评论

共 0 个回答

按投票排序 按时间排序
页数为0!!

我来回答

我在编程中遇到了一个问题?

我要提问

社区规则

1.不允许发布任何推广信息、非IT相关问题以及没有意义的灌水贴

2.提问标题清晰明了,内容尽可能详细,通过截图和代码将问题描述清楚

3.回答需经过自己仔细思考,不一定要正确,但一定要负责任

4.使用过程中遇到问题,或者有任何意见建议,可以通过新浪微博 @程序员俱乐部 私信反馈