Java --- redis7之redisson的使用

news2024/11/28 9:41:20

目录

一、自研分布式锁核心

 二、redlock红锁算法 Distributed locks with redis

2.1、设计理念

2.2、容错公式

2.3、单机案例实现 

三、redisson源码分析

四、多机案例

4.1、启动三台docker的redis7

4.2、进入redis

一、自研分布式锁核心

1、按照JUC里面java.util.concurrent.locks.Lock接口规范编写

2、lock加锁:

         ①、加锁:在redis中给key设置一个值,为避免死锁,并给定过期时间

         ②、自旋:加锁失败,需要使用while进行重试并自旋

         ③、续期:自动续期,定期监控扫描

3、unlock解锁:将属于自己的key进行删除。

 二、redlock红锁算法 Distributed locks with redis

2.1、设计理念

 条件1:客户端从超过半数(大于等于N/2+1)的redis实例上成功获得了锁。

 条件2:客户端获取锁的总耗时没有超过锁的有效时间。

2.2、容错公式

redis只支持AP,为了解决CP的风险,采用N个节点,N为奇数,三个master各自完全独立,不是主从或集群。

容错公式:

N = 2X + 1(N是最终部署机器数,X是容错机器数)

容错:

失败多少个机器实例后都可以容忍,即数据一致性还是OK的,CP数据一致性还是可以满足。

如:加入在集群环境中,redis失败1台,可以接受,2 * 1 + 1 = 3,部署3台,宕机1台剩2台可以正常工作,就部署3台。

为什是奇数:

最少的机器,最多的产生效果。

2.3、单机案例实现 

1、导入pom依赖

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

 config配置类

 @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.200.110:6379")
                .setDatabase(0)
                .setPassword("123456");
      return (Redisson) Redisson.create(config);
    }
 @Autowired
    private Redisson redisson;
@Override
    public String saleRedisson() {
        String resultMessage = "";
        //加锁
        RLock redissonLock = redisson.getLock("redissonLock");
        redissonLock.lock();
        try {
            //查询redis里的库存
            String result = stringRedisTemplate.opsForValue().get(IN_KEY + 1);
            //判断库存是否足够
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //卖出商品扣减库存
            if (inventoryNumber > 0){
                //存入redis
                stringRedisTemplate.opsForValue().set(IN_KEY + 1,String.valueOf(--inventoryNumber));
                resultMessage = "成功卖出商品,库存剩余:" + inventoryNumber;
                log.info(resultMessage + "\t服务端口号:{}",port);
            }else {
                resultMessage = "商品已售空";
            }
        } finally {
            //解锁,需要进行判断,只能删除属于当前线程的锁,不能删除别人的锁
            //必须加判断防止生成上出现BUG!!!
            if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()){
                redissonLock.unlock();
            }
        }
        return resultMessage;
    }
@ApiOperation("redisson扣减库存")
    @RequestMapping(value = "/saleRedisson",method = RequestMethod.GET)
    public String saleRedisson(){
        return inventoryService.saleRedisson();
    }

三、redisson源码分析

redis分布式锁过期了,业务还在处理?续期

守护线程“续期”:

额外起一个线程,定期检查线程是否还持有锁,如果有则延长过期时间。

redisson里主要使用“看门狗”定期检查(每1/3的锁时间检查1次),如果线程还持有锁,则刷新过期时间。

在获取锁成功后,给锁加一个watchdong,watchdog会起一个定时任务,在锁没有被释放且快要过期的时侯会续期。

源码分析:

1、使用redisson新建的锁key,默认是30秒

 2、

 3、

①、通过exists判断,如果锁不存在,则设置值和过期时间,加锁成功。

②、通过hexists判断,如果锁已存在,并且锁的是当前线程,则证明是重入锁,加锁成功。

③、如果锁已存在,但锁的不是当前线程,则证明有其他线程持有锁。返回当前锁的过期时间(代表了锁key的剩余生存时间),加锁失败。

 4、

 初始化一个定时器,dely的时间是internalLockLeaseTime/3

在redisson中,internalLockLeaseTime是30s,每隔10s续期一次,每次30s

加锁成功就会启动一个watch dog看门狗,他是一个后台线程,会每隔10秒检查一下,如果客户端A还持有锁key,那么就会不断地延长锁key的生存时间,默认每次续命又从30秒新开始。 

 自动续期lua脚本

解锁:

四、多机案例

4.1、启动三台docker的redis7

docker run -p 6381:6379 --name redis_master1 -d redis:7.0.0

docker run -p 6382:6379 --name redis_master2 -d redis:7.0.0

docker run -p 6383:6379 --name redis_master3 -d redis:7.0.0

4.2、进入redis

docker exec -it redis_master1 redis-cli

4.3、案例实现

server.port=8090
spring.application.name=redissonTest
#swagger2
spring.swagger2.enabled=true
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
#redis配置
spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
spring.redis.mode=single

spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10

spring.redis.single.address1=192.168.200.110:6381
spring.redis.single.address2=192.168.200.110:6382
spring.redis.single.address3=192.168.200.110:6383
@Data
public class RedisSingleProperties {
    private String address1;
    private String address2;
    private String address3;
}
@Data
@ConfigurationProperties(prefix = "spring.redis",ignoreInvalidFields = false)
public class RedisProperties {
    private int database;
    //等待节点回复命令的时间,该时间从命令发送成功时开始计时
    private int timeout = 3000;
    private String password;
    private String mode;
    //池配置
    private RedisPoolProperties pool;
    //单机信息配置
    private RedisSingleProperties single;
}
@Data
public class RedisPoolProperties {
    private int maxIdle;
    private int minIdle;
    private int maxActive;
    private int maxWait;
    private int connTimeout = 10000;
    private int soTimeout;
    //池大小
    private int size;
}
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {
    @Autowired
    private RedisProperties redisProperties;
    @Bean
    public RedissonClient redissonClient1(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress1();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient2(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress2();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }
    @Bean
    public RedissonClient redissonClient3(){
        Config config = new Config();
        String node = redisProperties.getSingle().getAddress3();
        node = node.startsWith("redis://") ? node : "redis://" + node;
        SingleServerConfig singleServerConfig = config.useSingleServer()
                .setAddress(node)
                .setTimeout(redisProperties.getPool().getConnTimeout())
                .setConnectionPoolSize(redisProperties.getPool().getSize())
                .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
        if (StringUtils.isNotBlank(redisProperties.getPassword())){
            singleServerConfig.setPassword(redisProperties.getPassword());
        }
        return Redisson.create(config);
    }

}
@RestController
@Slf4j
public class RedLockController {
    public static final String CACHE_KEY_REDLOCK = "CJC_REDLOCK";
    @Autowired
    private RedissonClient redissonClient1;
    @Autowired
    private RedissonClient redissonClient2;
    @Autowired
    private RedissonClient redissonClient3;
    @GetMapping("/getMultiLock")
    public String  getMultiLock(){
        String threadName = Thread.currentThread().getId()+"";
        RLock lock1 = redissonClient1.getLock(CACHE_KEY_REDLOCK);
        RLock lock2 = redissonClient2.getLock(CACHE_KEY_REDLOCK);
        RLock lock3 = redissonClient3.getLock(CACHE_KEY_REDLOCK);
        RedissonMultiLock redissonMultiLock = new RedissonMultiLock(lock1, lock2, lock3);
        redissonMultiLock.lock();
        try {
            log.info("com in getMultiLock",threadName);
            try {
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("com over getMultiLock",threadName);

        } catch (Exception e){
            e.printStackTrace();
            log.info("getMultiLock出现异常:{}",e.getCause() + "\t"+ e.getMessage());
        } finally{
            redissonMultiLock.unlock();
            log.info("释放分布式锁成功:{}",CACHE_KEY_REDLOCK);
        }
        return "multilock task is over " + threadName;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537710.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux kernel pwn

基础知识 内核概述 内核架构 通常来说我们可以把内核架构分为两种&#xff1a;宏内核和微内核&#xff0c;现在还有一种内核是混合了宏内核与微内核的特性&#xff0c;称为混合内核。 宏内核&#xff08;Monolithic kernel&#xff09;&#xff0c;也译为集成式内核、单体…

工信部认可! 开源网安“源码级软件开发安全解决方案”成功入选优秀方案

日前&#xff0c;开源网安“源码级软件开发安全解决方案”成功入选工信部网络安全产业发展中心“2022年信息技术应用创新解决方案”&#xff0c;成为经工业部认可的优秀解决方案。 据悉&#xff0c;由工业和信息化部网络安全产业发展中心&#xff08;工业和信息化部信息中心&am…

【Leetcode刷题】算法:罗马数字转整数

文章目录 一、问题二、代码理解 一、问题 二、代码理解 class Solution:def romanToInt(self, s: str) -> int:answer0length len(s)d{I:1,V:5,X:10, L:50,C:100, D:500,M:1000}for i in range(length-1):if d[s[i1]]>d[s[i]]:answeranswer-d[s[i]]else:answeranswerd[…

2023年市场规模将超147亿美元,中国人工智能产业的“风口”来了吗?

2023年IDC中国ICT市场趋势论坛于5月10日召开&#xff0c;会议重点探讨了人工智能、工业互联网、网络安全、大数据、云计算等领域&#xff0c;并强调了智能终端、智慧城市和半导体等行业的前景。 IDC预计&#xff0c;中国人工智能市场规模在2023年将超过147亿美元&#xff0c;到…

Day2 排序子序列、倒置字符串

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C/C相关题解 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 选择题1.字符串/C指针2.计算机组成原理 编程题1.排序子序列2.倒置字符串 选择题 1.字符串/C指针 题目&#xff…

【网络协议详解】——数据链路层协议(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层是 OSI 模型中的第二层&#xff0c;位于物理层之上&#xff0c;是通信网络中的重要组成部分之一。数据链路层协议负责将网络层传输的数据分组封装成帧&#xff0c;传输到物理层&#xff0c;并通过物理介质进行传输。同时&#xff0c;数…

为什么有些同事昨天还干的好好地,今天就离职了老板都留不住?

HR时常会遭遇员工突发申请离职&#xff0c;对于一些核心岗位员工的离职&#xff0c;甚至没有时间去挽留。 但从心理学的角度来说&#xff0c;人的行为与意识是相互反应、互加映衬的。 也就是说&#xff0c;员工离职意识的产生与发展&#xff0c; 与他近期的行为息息相关。 与…

零基础学软件测试怎么样? 完好就业吗

在当今数字化快速发展的社会中&#xff0c;软件行业已经成为了一个具有巨大潜力和广阔前景的行业。而软件测试作为软件开发过程中不可或缺的一部分&#xff0c;也因此成为了备受瞩目的职业之一。 对于零基础的人来说&#xff0c;学习软件测试是一项非常实用的技能&#xff0c;…

PyQt5开发入门到IP查询工具实现

1 基本介绍 1.1 简介 ,QT 是最强大的 GUI 库之一&#xff0c;PyQt 是 Python 绑定 QT 应用的框架,是最强大和流行的跨平台 GUI 库之一。 PyQt 兼容所有流行的操作系统&#xff0c;包括 Windows、Linux 和 Mac OS。 它是双重许可的&#xff0c;可在 GPL 和商业许可下使用。新…

stm32f407单片机上通过HAL库实现can总线数据的收发

最近在使用can总线&#xff0c;由于这个以前接触的比较少&#xff0c;所以调试代码的时候直接是下载的正点原子的例程&#xff0c;在这个基础上修改调试的。现在将调试中遇到的问题&#xff0c;总结一下&#xff0c;避免以后踩坑。目前写了一个查询方式的&#xff0c;一个中断方…

Kubernetes第4天

第六章 Pod控制器详解 本章节主要介绍各种Pod控制器的详细使用。 Pod控制器介绍 Pod是kubernetes的最小管理单元&#xff0c;在kubernetes中&#xff0c;按照pod的创建方式可以将其分为两类&#xff1a; 自主式pod&#xff1a;kubernetes直接创建出来的Pod&#xff0c;这种p…

Nginx的原理

Nginx的原理 1、mater 和 worker2、worker 如何进行工作的3、一个 master 和多个 woker 有好处4、设置多少个 woker 合适5、连接数 worker_connection 1、mater 和 worker 2、worker 如何进行工作的 3、一个 master 和多个 woker 有好处 &#xff08;1&#xff09;可以使用 ng…

Kubernetes第5天

第七章 Service详解 本章节主要介绍kubernetes的流量负载组件&#xff1a;Service和Ingress。 Service介绍 ​ 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着…

【计算机网络详解】——数据链路层(学习笔记)

&#x1f4d6; 前言&#xff1a;数据链路层提供了一种在不可靠的物理介质上传输数据的方式&#xff0c;并负责在网络层和物理层之间提供一个可靠的通信连接。本文将对数据链路层进行详细的介绍&#xff0c;包括数据链路层的定义、协议、功能和应用等方面。 目录 &#x1f552; …

Jenkins持续集成之jenkins安装入门教学

Jenkins安装 1、下载jenkins&#xff1b;官方地址&#xff1a;https://www.jenkins.io/ 2、点击Download 3、下载windows版本的安装包后缀为msi&#xff1b; 4、双击安装&#xff1b;如下图 5、安装到指定的盘&#xff1b;再点击next 6、勾选第一个框&#xff1b;再点击next 7…

《Netty》从零开始学netty源码(五十八)之NioEventLoop.execute()

目录 NioEventLoop.execute()addTask()startThread()NioEventLoop.run()select()处理keys与执行任务processSelectedKeys()处理AbstractNioChannelselectAgain() runAllTasks()fetchFromScheduledTaskQueue()runAllTasksFrom()afterRunningAllTasks() 带截止时间的runAllTasks(…

国考省考行测:图形推理题1,2平移,旋转,翻转

国考省考行测&#xff1a;图形推理题1,2平移&#xff0c;旋转&#xff0c;翻转 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff…

[数据结构 -- C语言] 栈(Stack)

目录 1、栈 1.1 栈的概念及结构 2、栈的实现 2.1 接口 3、接口的实现 3.1 初始化 3.2 入栈/压栈 3.3 出栈 3.4 获取栈顶元素 3.5 获取栈中有效元素个数 3.6.1 bool 类型接口 3.6.2 int 类型接口 3.7 销毁栈 4、完整代码 5、功能测试 1、栈 1.1 栈的概念及结构 …

软件测试工程师简历要怎么写,才能让HR看到

作为软件测试的从业者&#xff0c;面试或者被面试都是常有的事。 可是不管怎样&#xff0c;和简历有着理不清的关系&#xff0c;面试官要通过简历了解面试者的基本信息、过往经历等。】、 如果你不知道软件测试简历怎么写&#xff0c;可以看看这个视频是怎么写的&#xff0c;…

ARM-底层/Day2

.text .global _start _start:mov r0,#9mov r1,#15bl cmp_funccmp_func:cmp r0,r1beq stop 相等则跳转结束 subhi r0,r0,r1subcc r1,r1,r0mov pc,lr 不相等则返回执行 stop: b stop .end 循环实现1~100之间的和 .text .global _start _start:mov r0,#0mov r1,#1bl sum_fun…