Spring Boot 集成Redisson实现分布式锁详细案例

作者:剑圣无痕??????? 时间:2023-06-10 05:09:17 

前言

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的版本对应。

具体对应的关系如下:

Spring Boot 集成Redisson实现分布式锁详细案例

注意: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:7004"
         - "redis://127.0.0.1:7001"
         - "redis://127.0.0.1:7000"
         scanInterval: 1000
         pingConnectionInterval: 0
         keepAlive: false
         tcpNoDelay: false
       threads: 16
       nettyThreads: 32
       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);
           }
       });
   }

执行结果如下:

Spring Boot 集成Redisson实现分布式锁详细案例

来源:https://juejin.cn/post/7128050664336261157

标签:Spring,Boot,集成,Redisson,分布式,锁
0
投稿

猜你喜欢

  • c# List和Dictionary常用的操作

    2023-06-14 15:44:32
  • java线程中断 interrupt 和 LockSupport解析

    2021-06-29 01:20:48
  • Java编程使用卡片布局管理器示例【基于swing组件】

    2022-03-03 10:46:33
  • Spring-boot 2.3.x源码基于Gradle编译过程详解

    2022-04-07 15:41:36
  • Android模仿知乎的回答详情页的动画效果

    2022-12-25 06:08:57
  • SpringBoot 导出数据生成excel文件返回方式

    2023-09-01 11:29:27
  • java实现将结果集封装到List中的方法

    2021-10-27 22:29:45
  • Java实战之实现用户登录

    2022-08-03 14:42:55
  • C# 快速高效率复制对象(表达式树)

    2023-08-15 18:07:12
  • SpringBoot使用Druid数据源的配置方法

    2023-08-19 19:52:11
  • Java CompletableFuture 异步超时实现深入研究

    2022-12-28 04:18:14
  • 移动端WebApp隐藏地址栏的方法

    2022-09-26 20:26:29
  • .NET中的Timer类型用法详解

    2022-12-15 18:33:23
  • Spring使用注解存储和读取对象详解

    2022-06-10 00:35:24
  • Activity配置、启动和关闭activity实例详解

    2022-12-09 04:10:57
  • java扩展Hibernate注解支持java8新时间类型

    2021-09-12 07:43:38
  • maven中下载jar包源码和javadoc的命令介绍

    2023-07-27 04:41:01
  • 详解基于Android App 安全登录认证解决方案

    2022-12-28 04:45:18
  • 替换so文件来动态替换Flutter代码实现详解

    2023-06-23 16:24:06
  • java贪吃蛇游戏编写代码

    2023-06-16 02:41:10
  • asp之家 软件编程 m.aspxhome.com