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

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

分布式锁实现

引入jar包

      org.springframework.boot    spring-boot-starter-data-redis                   io.lettuce         lettuce-core                                org.redisson         redisson-spring-boot-starter         3.13.6     

说明:关于集成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: ! {}transportMode: "NIO"

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

 clusterServersConfig:          idleConnectionTimeout: 10000          connectTimeout: 10000          timeout: 3000          retryAttempts: 3          retryInterval: 1500          failedSlaveReconnectionInterval: 3000          failedSlaveCheckInterval: 60000          password: null          subscriptionsPerConnection: 5          clientName: null          loadBalancer: ! {}          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: ! {}        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 lock(String key, Supplier 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 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
来源:稀土掘金

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章