当前位置: 首页 > article >正文

学习Redisson实现分布式锁

官网:https://redisson.org/

官方文档:https://redisson.org/docs/getting-started/

官方中文文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

1、引入依赖

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.6</version>
</dependency>

如果用下面这个依赖,后面则省去config这一步配置过程,如果用上面后面config则要配置

<!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.27.2</version>
</dependency>

2、配置Redisson客户端

(1)程序化配置方法

参考官网:

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient(){
        // 配置
        Config config = new Config();
        // 如果是集群使用config.useClusterServers().setAddress()
        config.useSingleServer().setAddress("redis://192.168.75.101:6379").setPassword("123321");
        // 创建RedissonClient对象
        return Redisson.create(config);
    }
}
(2)文件方式配置

当然也可以如果yml的方式进行配置,参考官方文档如下:https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#22-%E6%96%87%E4%BB%B6%E6%96%B9%E5%BC%8F%E9%85%8D%E7%BD%AE

还有一些配置详细信息:

 这里我们使用程序化配置方式 !

3、简单使用Redisson的分布式锁

这里讲可重入锁

 参考:8. 分布式锁和同步器 · redisson/redisson Wiki · GitHub

@Resource
private RedissionClient redissonClient;

@Test
void testRedisson() throws Exception{
    //获取锁(可重入),指定锁的名称
    RLock lock = redissonClient.getLock("myLock");
    //尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位
    boolean isLock = lock.tryLock(1,10,TimeUnit.SECONDS);
    //判断获取锁成功
    if(isLock){
        try{
            System.out.println("执行业务");          
        }finally{
            //释放锁
            lock.unlock();
        }
        
    }       
}

Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟

剩余其余锁可以参考官方文档。

4、分布式锁原理

Redisson分布式锁原理:

可重入:利用hash结构记录线程id和重入次数

可重试:利用信号量和PubSub功能实现等待、唤醒,获取锁失败的重试机制

超时续约:利用watchDog机制,开启一个定时任务,每隔一段时间(releaseTime / 3),重置超时时间

5、实战

在微信登录操作的时候加上分布式锁

首先需要:

@Resource
private RedissonClient redissonClient;
/**
     * 微信登录
     */
    @Override
    public User wxLogin(UserLoginDTO userLoginDTO) {
        //调用微信接口服务,获得当前微信用户的openid
        String openid = getOpenid(userLoginDTO.getCode());
        System.out.println("当前微信用户的openid"+openid);

        //判断openid是否为空,如果为空表示登录失败,抛出业务异常
        if (openid==null){
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }

        //判断当前用户是否为新用户
//        User user = userMapper.getByOpenid(openid);
        QueryWrapper<User>wrapper=new QueryWrapper<>();
        wrapper.lambda()
                .eq(User::getOpenid,openid);
        User user = userMapper.selectOne(wrapper);

        //如果为新用户,自动完成注册
        if (user==null){
            RLock lock = redissonClient.getLock(RedissonConstant.LOCK_USER_REGISTER_KEY + openid);
            if (!lock.tryLock()){
                throw new RegisterFailedException(MessageConstant.REGISTER_FAILED_USER_EXIST);
            }
            try {
                //获得当前用户表数据数量
                Long allUserNums = userMapper.selectCount(null);
                //构造用户编号id
                /*"%0" + 8 + "d":这是一个格式字符串,用于指定输出格式。
                "%0" 表示输出时,如果数字的长度不足指定的宽度,则在前面补0;
                8 是紧接着 %0 后面的数字,表示输出的数字至少要有8位;
                "d" 表示输出的是十进制整数。
                因此,整个格式字符串意味着输出一个至少8位的十进制整数,如果不足8位,则在前面补0*/
                String substring = String.valueOf(System.currentTimeMillis()).substring(5);
                String quickUserId = "QUICK" +substring+"_"+ String.format("%0" + 6 + "d", allUserNums + 1);
                log.info("注册新用户");
                user=User.builder()
                        .openid(openid)
                        .quickUserId(quickUserId)
                        .wallet(0L)
                        .follow(0L)
                        .fan(0L)
                        .briefIntroduction("")
                        .collectNumber(0L)
                        .markNumber(0L)
                        .useTime(0L)
                        .name("微信用户")
                        .phone("")
                        .sex("")
                        .avatar("https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0")
                        .createTime(LocalDateTime.now())
                        .build();
                userMapper.insert(user);
                //发送mq异步消息修改es表 发送这两个数据后续获取用户信息 openid
                try {
                    rabbitTemplate.convertAndSend(
                            EsUserDocListener.ADD_USER_DOC_EXCHANGE_NAME,
                            EsUserDocListener.ADD_USER_DOC_ROUTING_KEY,
                            user.getQuickUserId());
                } catch (AmqpException e) {
                    log.error("发送新增userDoc消息失败", e);
                }
            }catch (DuplicateKeyException e){
                throw new RegisterFailedException(MessageConstant.REGISTER_FAILED_USER_EXIST);
            }finally {
                // 解锁
                lock.unlock();
            }
        }

        //返回这个用户的对象

        return user;
    }


http://www.kler.cn/news/353882.html

相关文章:

  • 【Linux系统编程】环境基础开发工具使用
  • ⭐ Unity Pico PXR_SDK转场淡入淡出
  • kimi帮我解决ubuntu下软链接文件夹权限不够的问题
  • 基础数据结构——用递归完成冒泡排序
  • 【大数据】HDFS DataNode节点下线
  • IOS每次查询数据时,当前滚动的位置会跑到底部?
  • wsl: 检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理的解决方法
  • Python脚本之获取Splunk数据发送到第三方UDP端口
  • java01作业说明:
  • 机器学习:情感分析的原理、应用场景及优缺点介绍
  • 对TCP/IP、HTTP协议原理的分析和总结
  • C++学习笔记----9、发现继承的技巧(一)---- 使用继承构建类(4)
  • 雷达手势识别技术
  • C++ IO多路复用 poll模型
  • 前端学习-CSS的三大特性(十七)
  • python 爬虫 入门 一、基础工具
  • 分布式数据库环境(HBase分布式数据库)的搭建与配置
  • [算法日常] 逆序对
  • 音乐播放器项目专栏介绍​
  • Linux的kafka安装部署