写点什么

阿里大牛详细讲解:Spring Boot 集成 Redisson 实现分布式锁

  • 2023-02-28
    湖南
  • 本文字数:4040 字

    阅读完需:约 13 分钟

针对单机分布式锁还是存在锁定续期、可重入的问题,本文将采用 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 的版本对应。具体对应的关系如下:


注意: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: 1threads: 0nettyThreads: 0codec: !<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 工具类

@Componentpublic 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 的基本使用,关于 Redisson 源码的分析将在后续的文章中进行讲解,如有疑问,请随时反馈。


作者:剑圣无痕

链接:https://juejin.cn/post/7128050664336261157

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
阿里大牛详细讲解:Spring Boot 集成Redisson实现分布式锁_Java_做梦都在改BUG_InfoQ写作社区