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

目录
  • 前言
  • 分布式锁实现
    • 引入jar包
  • Redisson的配置
    • application.yml中引入redisson.yml配置
    • redisson.yml配置
  • 封装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的版本对应。

具体对应的关系如下:

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

(0)

相关推荐

  • SpringBoot集成Redisson实现分布式锁的方法示例

    上篇 <SpringBoot 集成 redis 分布式锁优化>对死锁的问题进行了优化,今天介绍的是 redis 官方推荐使用的 Redisson ,Redisson 架设在 redis 基础上的 Java 驻内存数据网格(In-Memory Data Grid),基于NIO的 Netty 框架上,利用了 redis 键值数据库.功能非常强大,解决了很多分布式架构中的问题. Github的wiki地址: https://github.com/redisson/redisson/wiki 官方文档

  • SpringBoot集成Redisson实现延迟队列的场景分析

    使用场景 1.下单成功,30分钟未支付.支付超时,自动取消订单 2.订单签收,签收后7天未进行评价.订单超时未评价,系统默认好评 3.下单成功,商家5分钟未接单,订单取消 4.配送超时,推送短信提醒 ...... 对于延时比较长的场景.实时性不高的场景,我们可以采用任务调度的方式定时轮询处理.如:xxl-job 今天我们采用一种比较简单.轻量级的方式,使用 Redis 的延迟队列来进行处理.当然有更好的解决方案,可根据公司的技术选型和业务体系选择最优方案.如:使用消息中间件Kafka.Rabbi

  • spring boot集成redisson的最佳实践示例

    目录 前言 集成jedis实例,xml方式 集成前引用的jar springbean配置xml 集成redisson实例,javabean的方式 集成前引入的jar javabean配置如下 提供实例化javabean application.properties添加如下配置 前言 本文假使你了解spring boot并实践过,非spring boot用户可跳过也可借此研究一下. redisson是redis的java客户端程序,国内外很多公司都有在用,如下, 和spring的集成中官方给出的实

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

    目录 前言 分布式锁实现 引入jar包 Redisson的配置 application.yml中引入redisson.yml配置 redisson.yml配置 封装Redisson工具类 模拟秒杀扣减库存 测试代码 总结 前言 Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期.可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解. 分布式锁实现 引入jar包 <dependency> <groupId>org

  • Spring boot 整合 Redisson实现分布式锁并验证功能

    目录 简述 1. 在idea中新建spring boot工程并引入所需依赖 2. 编写相关代码实现 3. 模拟实际环境验证 3.1 下载idea的docker插件并配置相关镜像信息 3.2 将spring boot打包的jar构建为docker镜像 3.2 配置nginx 3.3 下载安装Jmeter进行测试 简述 整篇文章写的比较粗糙,大佬看了轻喷.前半部分 是整合spring boot和redisson, 后半部分是验证分布式锁.在整个过程中遇见了不少的问题,在此做个记录少走弯路 redis

  • Spring Boot整合Zookeeper实现分布式锁的场景分析

    目录 一.Java当中关于锁的概念 1.1.什么是锁 1.2.锁的使用场景 1.3.什么是分布式锁 1.4.分布式锁的使用场景 二.zk实现分布式锁 2.1.zk中锁的种类: 2.2.zk如何上读锁 2.3.zk如何上写锁 2.4.⽺群效应 三.springboot整合分布式锁 温馨提示:本篇文章要求掌握zk的数据结构,以及临时序号节点! zk实现分布式锁完全是依靠zk节点类型当中的临时序号节点来实现的 一.Java当中关于锁的概念 1.1.什么是锁 锁是用来控制多个线程访问共享资源的方式,一般

  • Spring Boot 集成 Kafkad的实现示例

    Spring Boot 作为主流微服务框架,拥有成熟的社区生态.市场应用广泛,为了方便大家,整理了一个基于spring boot的常用中间件快速集成入门系列手册,涉及RPC.缓存.消息队列.分库分表.注册中心.分布式配置等常用开源组件,大概有几十篇文章,陆续会开放出来,感兴趣同学请提前关注&收藏 消息通信有两种基本模型,即发布-订阅(Pub-Sub)模型和点对点(Point to Point)模型,发布-订阅支持生产者消费者之间的一对多关系,而点对点模型中有且仅有一个消费者. 前言 Kafka是

  • Spring Boot集成ShedLock分布式定时任务的实现示例

    一.ShedLock是什么? 官方地址:github.com/lukas-kreca- 以下是ShedLock锁提供者,通过外部存储实现锁,由下图可知外部存储集成的库还是很丰富的 本篇教程我们基于JdbcTemplate存储为例来使用ShedLock锁. 二.落地实现 1.1 引入依赖包 shedlock所需依赖包: <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>she

  • spring boot集成shiro详细教程(小结)

    我们开发时候有时候要把传统spring shiro转成spring boot项目,或者直接集成,name我们要搞清楚一个知识,就是 xml配置和spring bean代码配置的关系,这一点很重要,因为spring boot是没有xml配置文件的(也不绝对,spring boot也是可以引用xml配置的) 引入依赖: <dependency> <artifactId>ehcache-core</artifactId> <groupId>net.sf.ehcac

  • Java Spring Boot 集成Zookeeper

    目录 集成步骤 1.pom.xml文件配置,引入相关jar包 2. 核心配置类 3.常用API功能 4.基本使用 常见错误和解决办法 问题1:调用api创建zookeeper节点时,报KeeperErrorCode = Unimplemented for /test错误. 问题2:启动项目的日志中会有Will not attempt to authenticate using SASL错误 集成步骤 1.pom.xml文件配置,引入相关jar包 Curator是Netflix公司开源的一套zoo

  • Java编程redisson实现分布式锁代码示例

    最近由于工作很忙,很长时间没有更新博客了,今天为大家带来一篇有关Redisson实现分布式锁的文章,好了,不多说了,直接进入主题. 1. 可重入锁(Reentrant Lock) Redisson的分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁. public void testReentrantLock(RedissonClient redisson){ RLock lock = redisson.getL

随机推荐