阅读 144

Spring Boot 集成Redisson实现分布式锁

前言

上一章讲解了Spring Boot集成Redis实现单机分布式锁,但是针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解。

分布式锁实现

引入jar包

  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId>     <exclusions>       <exclusion>          <groupId>io.lettuce</groupId>          <artifactId>lettuce-core</artifactId>       </exclusion>       </exclusions>    </dependency>          <dependency>          <groupId>org.redisson</groupId>          <artifactId>redisson-spring-boot-starter</artifactId>          <version>3.13.6</version>      </dependency> 复制代码

说明:关于集成Redisson,我们需要注意与Spring Boot的版本对应。具体对应的关系如下:

图片.png

注意:3.13.6对应的Spring Boot的版本为2.3.0,而redis-spring-data为redis-spring-data-23。我们可以通过查看pom文件的引用从而得到依赖关系。

Redisson的配置

application.yml中引入redisson.yml配置

  redis:     redisson:       file: classpath:redisson.yml 复制代码

redisson.yml配置

singleServerConfig:   password: xxxx   address: "redis://127.0.0.1:6379"   database: 1 threads: 0 nettyThreads: 0 codec: !<org.redisson.codec.FstCodec> {} transportMode: "NIO" 复制代码

说明:本文配置的是单机环境,如果需要配置集群环境,可以采用如下配置:

 clusterServersConfig:           idleConnectionTimeout: 10000           connectTimeout: 10000           timeout: 3000           retryAttempts: 3           retryInterval: 1500           failedSlaveReconnectionInterval: 3000           failedSlaveCheckInterval: 60000           password: null           subscriptionsPerConnection: 5           clientName: null           loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}           subscriptionConnectionMinimumIdleSize: 1           subscriptionConnectionPoolSize: 50           slaveConnectionMinimumIdleSize: 24           slaveConnectionPoolSize: 64           masterConnectionMinimumIdleSize: 24           masterConnectionPoolSize: 64           readMode: "SLAVE"           subscriptionMode: "SLAVE"           nodeAddresses:           - "redis://127.0.0.1:6379"           - "redis://127.0.0.1:6380"           - "redis://127.0.0.1:6381"           scanInterval: 1000           pingConnectionInterval: 0           keepAlive: false           tcpNoDelay: false         threads: 6         nettyThreads: 12         codec: !<org.redisson.codec.MarshallingCodec> {}         transportMode: "NIO" 复制代码

封装Redisson工具类

@Component public class RedissonLockUtil {     private static final Logger logger = LoggerFactory.getLogger(RedissonLockUtil.class);              @Autowired         private RedissonClient redissonClient;               /**          * 加锁          * @param lockKey          * @return          */         public RLock lock(String lockKey)          {             RLock lock = redissonClient.getLock(lockKey);             return lock;         }               /**          * 公平锁          * @param key          * @return          */         public RLock fairLock(String key)          {             return redissonClient.getFairLock(key);         }                  /**          * 带超时的锁          * @param lockKey          * @param timeout 超时时间 单位:秒          */         public RLock lock(String lockKey, int timeout)          {             RLock lock = redissonClient.getLock(lockKey);             lock.lock(timeout, TimeUnit.SECONDS);             return lock;         }                  /**          * 读写锁          * @param key          * @return          */         public RReadWriteLock readWriteLock(String key) {             return redissonClient.getReadWriteLock(key);         }               /**          * 带超时的锁          * @param lockKey          * @param unit 时间单位          * @param timeout 超时时间          */         public RLock lock(String lockKey, TimeUnit unit ,int timeout) {             RLock lock = redissonClient.getLock(lockKey);             lock.lock(timeout, unit);             return lock;         }               /**          * 加锁          * @param key          * @param supplier          * @return          */         public <T> T lock(String key, Supplier<T> supplier) {             RLock lock = lock(key);             try {                 lock.lock();                 return supplier.get();             } finally {                 if (lock != null && lock.isLocked()) {                     lock.unlock();                 }             }         }               /**          * 尝试获取锁          * @param lockKey          * @param waitTime 等待时间          * @param leaseTime 自动释放锁时间          * @return          */         public  boolean tryLock(String lockKey, int waitTime, int leaseTime) {             RLock lock = redissonClient.getLock(lockKey);             try {                 return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);             } catch (InterruptedException e) {                 return false;             }         }               /**          * 尝试获取锁          * @param lockKey          * @param unit 时间单位          * @param waitTime 等待时间          * @param leaseTime 自动释放锁时间          * @return          */         public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {             RLock lock = redissonClient.getLock(lockKey);             try {                 return lock.tryLock(waitTime, leaseTime, unit);             } catch (InterruptedException e) {                 return false;             }         }               /**          * 释放锁          * @param lockKey          */         public void unlock(String lockKey) {             RLock lock = redissonClient.getLock(lockKey);             lock.unlock();         }                     /**          * 释放锁          * @param lock          */         public void unlock(RLock lock)          {             lock.unlock();         }     }   复制代码

模拟秒杀扣减库存

 public int lockStock()     {         String lockKey="lock:stock";         String clientId = UUID.randomUUID().toString();                  //加锁         RLock lock=redissonLockUtil.lock(lockKey);         lock.lock();                  try         {            logger.info("加锁成功 clientId:{}",clientId);            int stockNum= Integer.valueOf((String)redisUtil.get("seckill:goods:stock"));            if(stockNum>0)            {                  stockNum--;               redisUtil.set("seckill:goods:stock",String.valueOf(stockNum));               logger.info("秒杀成功,剩余库存:{}",stockNum);            }            else            {               logger.error("秒杀失败,剩余库存:{}", stockNum);            }            //获取库存数量            return stockNum;         }         catch (Exception e)         {            logger.error("decry stock eror",e);         }         finally         {             if(lock!=null)             {                 lock.unlock();              }         }         return 0;     } 复制代码

测试代码

@RequestMapping("/redisLockTest")     public void redisLockTest()     {         // 初始化秒杀库存数量         redisUtil.set("seckill:goods:stock", "10");                  List<Future> futureList = new ArrayList<>();                  //多线程异步执行         ExecutorService executors = Executors.newScheduledThreadPool(10);         //         for (int i = 0; i < 30; i++)         {             futureList.add(executors.submit(this::lockStock));                          try              {                Thread.sleep(100);             }              catch (InterruptedException e)              {                logger.error("redisLockTest error",e);             }         }                  // 等待结果,防止主线程退出         futureList.forEach(t -> {             try              {                 int stockNum =(int) t.get();                 logger.info("库存剩余数量:{}",stockNum);             }             catch (Exception e)             {                logger.error("get stock num error",e);             }         });     } 复制代码

执行结果如下:

图片.png

总结

本文针对Spring Boot集成Redisson的基本使用,关于Redisson源码的分析将在后续的文章中进行讲解,如有疑问,请随时反馈,


作者:剑圣无痕
链接:https://juejin.cn/post/7128050664336261157
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐