Springboot集成Redission实现分布式锁

前言

   随着系统的更新迭代,我们日常使用的synchronized与Reentrantlock已经不满足分布式系统的使用。Java 的 synchronized或者Reentrantlock 锁只能保证在同一个 JVM 进程内的多线程并发安全性。但是在多线程并发的情况下,并不能保证线程安全。因为现在的业务大多都是集群部署,也就是多个 JVM 实例,所以 Java 内置的锁无法保证集群环境的多线程安全性。所以如果业务是集群部署,那就需要分布式锁来保证整合集群的线程安全。

介绍

  Redission是一个基于Redis实现的Java分布式对象存储和缓存框架。它提供了丰富的分布式数据结构和服务。**例如:分布式锁、分布式队列、分布式Rate Limiter等。

分布式锁场景
  • 互联网秒杀
  • 抢优惠卷
  • 接口幂等性校验

 

特点
  • 互斥性是分布式锁的重要特点,在任意时刻,只有一个线程能够持有锁锁的超时时间;
  • 一个线程在持锁期间挂掉了而没主动释放锁,此时通过超时时间来保证该线程在超时后可以释放锁,这样其他线程才可以继续获取锁;
  • 加锁和解锁必须是由同一个线程来设置;
  • Redis 是缓存型数据库,拥有很高的性能,因此加锁和释放锁开销较小,并且能够很轻易地实现分布式锁。
     
特性 
  • 高性能

​  Redission是基于Redis的,因此它继承了Redis的高性能和低延迟的特性。同时,它采用了Netty的NIO框架,能够并发地处理大量的请求,使得应用程序的响应速度得到了极大的提升。

  • 易用性

​  Redission提供了丰富的API和方法,同时还提供了文档和示例,让开发者易于上手和使用。此外,它支持自动配置和灵活的配置方式,使得开发者可以根据自己的需求进行配置和调整。

  • 可扩展性

 ​ Redission的分布式架构使得它支持水平扩展,可以将数据和请求分散到更多的节点上进行处理。这也使得它具备了更好的容错能力和可靠性。

Redisson实现分布式锁

1、导入依赖

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.6.5</version>
        </dependency>

2、初始化客户端

package com.ruoyi.merit.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author : DongJiYong
 * @date : 2023-11-17 16:21
 */
@Configuration
public class RedissonConfig {

//    @Value("${redisson.address}")
//    private String address;
    @Bean
    public RedissonClient redisson(){
        // 单机模式
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
        return Redisson.create(config);
    }
}

3、 代码实现

目前导入的本人逻辑代码的实现



@Resource
private RedissonClient redissonClient;   



private ResultBody buildParamsAndSaveBatchByDb(List<MeritSzlsPdwFaultRuleConfig> meritSzlsPdwFaultRuleConfigList, String version, BigDecimal versionInteger, boolean flag) {
        // 1.获取锁对象
        RLock redissonLock = redissonClient.getLock("myLock");
       
        try {
            //5分钟之后锁失效
            //尝试获取锁,如果锁已经被其他线程获取,则等待直到获取到锁或者超时(第一个参数 等待时间 第二个参数 锁过期时间 第三个 时间单位)
            //注意:如果指定锁自动释放时间,不管业务有没有执行完,锁都不会自动延期,即没有 watch dog 机制。
            boolean locked = redissonLock.tryLock(-1,5, TimeUnit.MINUTES);
            // 从redis 中拿当前库存的值
            if (locked) {
                //业务处理
                for (MeritSzlsPdwFaultRuleConfig szlsPdwFaultRuleConfig : meritSzlsPdwFaultRuleConfigList) {
                    szlsPdwFaultRuleConfig.setGzVersion(version.substring(0, 1) + versionInteger);
                    szlsPdwFaultRuleConfig.setAutoId(null);
                }
                //新增版本+0.1
                flag = this.saveBatch(meritSzlsPdwFaultRuleConfigList);
                //根据版本号 + 检测项 设置数据库的MODEL_UPDATE状态为0
//                meritSzlsPdwFaultRuleConfigMapper.updateModelUpdate(meritSzlsPdwFaultRuleConfigList.get(0));
                /**
                 * 调用python脚本并传入版本号
                 */
//                usePythonScript(meritSzlsPdwFaultRuleConfigList, redissonLock);
            } else {
                log.info("程序正在执行中,请稍后重试");
                return ResultUtil.success("程序正在执行中,请稍后重试", "false");
            }
        } catch (Exception e) {
            log.error("操作异常", e);
            //报异常了需要关锁
            redissonLock.unlock();
        } finally {
            // 校验数据库ModelUpdate字段是否是 1 or 2
//            checkoutModelUpdateIsFinish(meritSzlsPdwFaultRuleConfigList, redissonLock);
            //注意! 目前是将调用python的代码注释了,如果放开注释 则需要把下面的Thread.sleep(2000)注释掉
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //查询当前线程是否持有此锁定,如果还持有,则释放,如果未持有,则说明已被释放;
            if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {
                redissonLock.unlock();
            }
        }
        // 关闭 Redisson 客户端
        return ResultUtil.success("操作完成", flag);
    }
实现原理 

使用步骤

分布式锁的使用分成以下 3 步:

  1. 获取锁:根据唯一的 key 去 redis 获取锁。

  2. 加锁:拿到锁后在指定的等待时间内不断尝试对其加锁,超过等待时间则加锁失败。

  3. 解锁:分成两种情形:

    • 第一如果在加锁的时候指定了自动释放时间,那么在此时间范围内业务提前完成的话就在 finally 手动释放锁,而如果业务没有完成也会自动释放锁,所以指定自动释放时间需要做非常仔细的考量;
    • 第二就是没有指定自动释放时间,由于 redisson 有 watch dog (看门狗)机制,watch dog 默认的 releaseTime 是 30s,给锁加上 30s 的自动释放时间,并且每隔 releaseTime / 3 即 10 s 去检查业务是否完成,如果没有完成重置 releaseTime 为 30 s, 即锁的续约,所以一个业务严重阻塞的话会造成系统资源的极大浪费。到这里你应该能够明白分布式锁是没有完美的解决方案的。
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2024暴富

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值