软件编程
位置:首页>> 软件编程>> java编程>> Spring Boot 集成Redisson实现分布式锁详细案例

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

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

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

具体对应的关系如下:

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

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com