SpringBoot日常:redission的接入使用和源码解析

文章目录

    • 一、简介
    • 二、集成redission
      • pom文件
      • redission 配置文件
      • application.yml文件
      • 启动类
    • 三、JAVA 操作案例
      • 字符串操作
      • 哈希操作
      • 列表操作
      • 集合操作
      • 有序集合操作
      • 布隆过滤器操作
      • 分布式锁操作
    • 四、源码解析

一、简介

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格客户端(In-Memory Data Grid)。它不仅提供了一系列的 redis 常用数据结构命令服务,还提供了许多分布式服务,例如分布式锁、分布式对象、分布式集合、分布式远程服务、分布式调度任务服务等等。
本文会介绍如何将redission集成到springboot和如何使用 Redisson 操作 Redis 中的字符串、哈希、列表、集合、有序集合,以及布隆过滤器和分布式锁等功能,并解析源码中其最大亮点的看门狗机制。

二、集成redission

本章内容使用的springboot版本为2.6.3,redission-starter版本为3.17.7

pom文件

引入redission相关依赖


<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-core</artifactId><version>2.12.3</version>
</dependency><dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-yaml</artifactId><version>2.12.3</version>
</dependency><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.7</version>
</dependency>

redission 配置文件

将redission相关的配置抽到单独的一个redission文件,放在application.yml的同级目录

# 单节点配置
singleServerConfig:# 连接空闲超时,单位:毫秒idleConnectionTimeout: 10000# 连接超时,单位:毫秒connectTimeout: 10000# 命令等待超时,单位:毫秒timeout: 3000# 命令失败重试次数,如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误。# 如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。retryAttempts: 3# 命令重试发送时间间隔,单位:毫秒retryInterval: 1500# 密码,没有设置密码时,需要注释掉,否则会报错# password: redis.shbeta# 单个连接最大订阅数量subscriptionsPerConnection: 5# 客户端名称clientName: "axin"# 节点地址address: "redis://192.168.102.111:6379"password: Cidneueopx# 发布和订阅连接的最小空闲连接数subscriptionConnectionMinimumIdleSize: 1# 发布和订阅连接池大小subscriptionConnectionPoolSize: 50# 最小空闲连接数connectionMinimumIdleSize: 32# 连接池大小connectionPoolSize: 64# 数据库编号database: 1# DNS监测时间间隔,单位:毫秒dnsMonitoringInterval: 5000
# 线程池数量,默认值: 当前处理核数量 * 2
threads: 0
# Netty线程池数量,默认值: 当前处理核数量 * 2
nettyThreads: 0
# 编码
codec: !<org.redisson.codec.JsonJacksonCodec> {}
# 传输模式
transportMode : "NIO"# 配置看门狗的默认超时时间为30s,这里改为 10s
lockWatchdogTimeout: 10000

application.yml文件

在该配置文件将redission配置文件引入

spring:redis:redisson:file: classpath:redisson.yml

启动类

启动类需要加上注解@ImportAutoConfiguration,如下所示

@SpringBootApplication
@ImportAutoConfiguration(RedissonAutoConfiguration.class)
public class EasyUserApplication {public static void main(String[] args) {SpringApplication.run(EasyUserApplication.class, args);}
}

三、JAVA 操作案例

字符串操作

Redisson 支持通过RBucket对象来操作字符串或对象(对象需要实现序列化Serializable)数据结构,同时支持设置数据和有效期,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opString")
public void opString() {RBucket<String> strKey = redissonClient.getBucket("strKey");strKey.set("china");//表示10分钟后删除该键strKey.expire(Duration.ofMinutes(10));System.out.println(strKey.get());
}

哈希操作

通过获取一个RMap 对象来进行哈希数据结构的操作,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opMap")
public void opMap() {//获取一个map对象RMap<String, String> rMap  = redissonClient.getMap("mapKey");rMap.put("wawa", "1212");//表示10分钟后删除该键rMap.expire(Duration.ofMinutes(10));System.out.println(rMap.get("wawa"));
}

列表操作

redission支持通过RList对象来操作列表数据结构,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opList")
public void opList() {RList<Integer> rList  = redissonClient.getList("listKey");rList.add(100);rList.add(200);rList.add(300);//表示10分钟后删除该键rList.expire(Duration.ofMinutes(10));//读取列表全部数据,不删除System.out.println(rList.readAll());
}

集合操作

Redisson支持通过RSet对象来操作集合,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opSet")
public void opSet() {RSet<Integer> rSet  = redissonClient.getSet("setKey");rSet.add(100);rSet.add(200);rSet.add(300);//表示10分钟后删除该键rSet.expire(Duration.ofMinutes(10));System.out.println(rSet.readAll());log.info("========testSkyworking");
}

有序集合操作

Redisson 支持通过RSortedSet对象来操作有序集合数据结构,如果使用存储对象,则实体对象必须先实现Comparable接口,并重写比较逻辑,否则会报错,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opSortSet")
public void opSortSet() {RSortedSet<Integer> rSortSet  = redissonClient.getSortedSet("sortsetKey");rSortSet.add(300);rSortSet.add(200);rSortSet.add(100);System.out.println(rSortSet.readAll());
}

布隆过滤器操作

Redisson支持通过RBloomFilter对象来操作布隆过滤器,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opBloomFilter")
public void opBloomFilter() {RBloomFilter rBloomFilter   = redissonClient.getBloomFilter("BloomFilterKey");// 初始化预期插入的数据量为50000和期望误差率为0.01rBloomFilter.tryInit(50000, 0.01);rBloomFilter.add(300);rBloomFilter.add(200);rBloomFilter.add(100);//表示100分钟后删除该键rBloomFilter.expire(Duration.ofMinutes(100));// 判断是否存在System.out.println(rBloomFilter.contains(300));System.out.println(rBloomFilter.contains(700));
}

分布式锁操作

其实很多时候,我们引入redission,最想使用的功能场景就是其分布式锁,因为我们不需要去设置最大释放锁的时间,redission内部有一个看门狗机制会主动去续期。
Redisson通过RLock对象来操作分布式锁,例子如下

@Autowired
RedissonClient redissonClient;@GetMapping("/opLock")
public void opLock() {//获取锁对象实例final String lockKey = "mylock";RLock rLock = redissonClient.getLock(lockKey);try {//尝试5秒内获取锁 不设置释放锁的时间boolean res = rLock.tryLock(5L,  TimeUnit.SECONDS);System.out.println("获取锁成功");if (res) {for (int i = 0; i < 10; i++) {System.out.println(i);Thread.sleep(1000L);}//成功获得锁,在这里处理业务System.out.println("处理业务");}} catch (Exception e) {System.out.println("获取锁失败,失败原因:" + e.getMessage());} finally {//无论如何, 最后都要解锁rLock.unlock();}
}

四、源码解析

分布式锁的看门狗机制大致如下列流程图所示
在这里插入图片描述
调用链路
org.redisson.RedissonLock#tryLock=》org.redisson.RedissonLock#tryLockAsync=》org.redisson.RedissonLock#tryAcquireAsync

我们来仔细看看org.redisson.RedissonLock#tryAcquireAsync

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture ttlRemainingFuture;//判断锁的持有时间是否由用户自定义if (leaseTime > 0L) {ttlRemainingFuture = this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {//当用户没有自定义锁占有时间时,默认传入 internalLockLeaseTime //private long lockWatchdogTimeout = 30 * 1000; 默认30秒ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}CompletionStage<Long> f = ttlRemainingFuture.thenApply((ttlRemaining) -> {if (ttlRemaining == null) {if (leaseTime > 0L) {//如果用户传入占用时间直接转换,把默认值internalLockLeaseTime 更新为用户自定义的占有时间this.internalLockLeaseTime = unit.toMillis(leaseTime);} else {//这里就是触发看门狗机制的方法,只有当 leaseTime == -1时才会触发看门狗机制this.scheduleExpirationRenewal(threadId);}}return ttlRemaining;});return new CompletableFutureWrapper(f);
}

接着进行scheduleExpirationRenewal

protected void scheduleExpirationRenewal(long threadId) {RedissonBaseLock.ExpirationEntry entry = new RedissonBaseLock.ExpirationEntry();//EXPIRATION_RENEWAL_MAP 是一个全局的静态常量MapRedissonBaseLock.ExpirationEntry oldEntry = (RedissonBaseLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.putIfAbsent(this.getEntryName(), entry);if (oldEntry != null) {//oldEntry != null 表示该线程不是第一次触发oldEntry.addThreadId(threadId);} else {//oldEntry == null 表示该线程是第一次触发entry.addThreadId(threadId);try {//更新过期时间this.renewExpiration();} finally {if (Thread.currentThread().isInterrupted()) {this.cancelExpirationRenewal(threadId);}}}}

org.redisson.RedissonBaseLock#renewExpiration中主要是更新时间的详细逻辑

private void renewExpiration() {//获取当前线程的更新对象RedissonBaseLock.ExpirationEntry ee = (RedissonBaseLock.ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());if (ee != null) {//创建了一个定时任务Timeout task = this.getServiceManager().newTimeout(new TimerTask() {public void run(Timeout timeout) throws Exception {RedissonBaseLock.ExpirationEntry ent = (RedissonBaseLock.ExpirationEntry)RedissonBaseLock.EXPIRATION_RENEWAL_MAP.get(RedissonBaseLock.this.getEntryName());if (ent != null) {Long threadId = ent.getFirstThreadId();if (threadId != null) {//异步更新过期时间  CompletionStage<Boolean> future = RedissonBaseLock.this.renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {//如果出现异常,从map中删除,直接返回RedissonBaseLock.log.error("Can't update lock {} expiration", RedissonBaseLock.this.getRawName(), e);RedissonBaseLock.EXPIRATION_RENEWAL_MAP.remove(RedissonBaseLock.this.getEntryName());} else {if (res) {//如果没有报错,就再次定时延期RedissonBaseLock.this.renewExpiration();} else {//否则取消定时RedissonBaseLock.this.cancelExpirationRenewal((Long)null);}}});}}}}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);ee.setTimeout(task);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT JetBrains2024&#xff08;IntelliJ IDEA、PhpStorm、RubyMine、Rider……&#xff09;安装包Anaconda Miniconda安装.condarc 文件配置镜像源查看conda的配置和源(channel)自定义conda虚拟环境路径conda常用命…

破解反编译:使用 ClassFinal 保护你的SpringBoot代码

在当今数字化时代&#xff0c;保护源代码的安全性变得愈发重要。无论是企业的核心算法还是独特的业务逻辑&#xff0c;代码一旦暴露&#xff0c;便可能导致竞争优势的丧失和商业机密的泄露。因此&#xff0c;在使用 Java 和 Spring Boot 开发项目时&#xff0c;理解从源代码到可…

websocket连接异常报错1006

目录&#xff1a; 1、问题现象2、问题原因3、解决方案 1、问题现象 WebSocket状态码的作用&#xff1a; 在WebSocket协议中&#xff0c;状态码用于表示连接状态和错误信息。通过状态码&#xff0c;我们可以快速判断连接是否成功&#xff0c;以及出现错误时的原因。常见的WebSo…

教培机构如何向知识付费转型

在数字化时代&#xff0c;知识付费已成为一股不可忽视的潮流。面对这一趋势&#xff0c;教育培训机构必须积极应对&#xff0c;实现向知识付费的转型&#xff0c;以在新的市场环境中立足。 一、教培机构应明确自身的知识定位。 在知识付费领域&#xff0c;专业性和独特性是关键…

VUE前后端分离毕业设计题目项目有哪些,VUE程序开发常见毕业论文设计推荐

目录 0 为什么选择Vue.js 1 Vue.js 的主要特点 2 前后端分离毕业设计项目推荐 3 后端推荐 4 总结 0 为什么选择Vue.js 使用Vue.js开发计算机毕业设计是一个很好的选择&#xff0c;因为它不仅具有现代前端框架的所有优点&#xff0c;还能让你专注于构建高性能、高可用性的W…

Matlab实现白鲸优化算法优化回声状态网络模型 (BWO-ESN)(附源码)

目录 1.内容介绍 2部分代码 3.实验结果 4.内容获取 1内容介绍 2部分代码 %% 清空环境变量 warning off % 关闭报警信息 close all % 关闭开启的图窗 clear % 清空变量 clc % 清空命令行 tic load bwand %%…

CC2530定时器1中断实现定时1-3

源码 #include "iocc2530.h"//引用CC2530头文件int t1_Count0; //定时器1溢出次数计数void Init_Led(void){ /*******************LED1初始化部分******************/P1SEL &~ 0x01; //设置P1_0口为通用I/O口P1DIR | 0x01; //设置P1_0口为输出口P…

软考越来越难了,2024年软考究竟还值不值得考?

最近不少同学沟通&#xff0c;聊到软考现在越来越难了&#xff0c;考了两三次都没过&#xff0c;也有不少新同学咨询软考考试的一些福利政策&#xff0c;投入大量的物力&#xff0c;财力&#xff0c;精力&#xff0c;那么到底软考值不值得考呢&#xff1f; 01 / 关于软考 软考…

Leetcode 10. 正则表达式匹配

1.题目基本信息 1.1.题目描述 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s 的&#xff0c;而不是部分…

k8s的控制节点不能访问node节点容器的ip地址

master控制node服务器添加容器后,访问不了该node服务器容器的ip,只能在node服务器访问 排查后发现是k8s的master服务器和node节点的网址网段和k8s初始化时提示的ip网段不一致 我之前是192.168.137.50, 实际上master主机期望的是192.168.1.50 解决方案: 1.删除服务器后重建ma…

python爬虫 - 进阶requests模块

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、SSL证书问题 &#xff08;一&#xff09;跳过 SSL 证书验证 &#xff0…

Vue3中提到的Tree-shaking

我们知道&#xff0c;Vue3中提到一个叫Tree-shaking的东西&#xff0c;其实也并不是一个新的东西&#xff0c;有人称之为"摇树优化"&#xff0c;什么意思&#xff1f; 按照作者的原话解释&#xff0c;Tree-shaking其实就是&#xff1a;把无用的模块进行“剪枝”&…

【Linux】进程间通信——System V消息队列和信号量

一、消息队列 1.1 概念 进程间通信的原理是让不同进程看到同一份资源&#xff0c;资源种类的不同就决定了通信方式的差异。如果用管道通信&#xff0c;则资源是文件缓冲区&#xff1b;如果用共享内存&#xff0c;则资源是内存块 消息队列是由操作系统提供的资源&#xff0c;…

postman自动化实战总结

Postman实战总结 简介 本次实战内容主要包括如下几点&#xff1a; l 背景介绍 l Postman使用&#xff0c;侧重于自动化实现&#xff0c;基础使用不做介绍 l 可视化Newman介绍 l 框架特色 l 实战中的坑 背景 随着国内软件技术的高速发展&#xff0c;越来越多的手工测试…

解决谷歌浏览器在安卓手机上的常见问题

在使用安卓手机浏览网页时&#xff0c;谷歌浏览器无疑是许多用户的首选。然而&#xff0c;在使用过程中&#xff0c;用户可能会遇到一些常见问题&#xff0c;如搜索图片困难、缓存积累过多导致浏览器卡顿&#xff0c;以及无法下载视频等。本文将针对这些问题&#xff0c;提供详…

【Linux】详解Linux下的工具(内含yum指令和vim指令)

文章目录 前言1. Linux下软件安装的方式2. yum2.1 软件下载的小知识2.2 在自己的Linux系统下验证yum源的存在2.3 利用yum指令下载软件2.4 拓展yum源&#xff08;针对于虚拟机用户&#xff09; 3. vim编辑器3.1 vim是什么&#xff1f;3.2 如何打开vim3.2 vim各模式下的讲解3.2.1…

【C语言】猜数字小游戏

&#x1f602;个人主页: 起名字真南 &#x1f923;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 随机数的生成1.1 rand1.2 srand1.3 time1.4 设置随机数范围 2 猜数字游戏实现 前言&#xff1a;我们学习完前面的循环以后可以写一个猜数字小游戏 1 随机数的生成 想要完成…

新生培训 day1 C语言基础 顺序 分支 循环 数组 字符串 函数

比赛地址 b牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ C语言数据类型 字符 整型数 int 2e9 long long 9e18 浮点数 代码示例 /** Author: Dduo * Date: 2024-10-8* Description: 新生培训day1 */ #include <stdio.h>int main() {// 定义变量in…

QT-空窗口主窗口对话框

1. QMainWindow QMainWindow 用来创建主窗口 主窗口包含&#xff1a; 标题栏&#xff08;Window title&#xff09;、菜单栏&#xff08;MenuBar&#xff09;、工具栏&#xff08;ToolBar&#xff09;、状态栏&#xff08;StatusBar&#xff09;、停靠部件&#xff08;DockWid…

Ansible学习之ansible-pull命令

想要知道ansible-pull是用来做什么的&#xff0c;就需要了解Ansible的工作模&#xff0c;Ansible的工作模式有两种&#xff1a; push模式 push推送&#xff0c;这是Ansible的默认模式&#xff0c;在主控机上编排好playbook文件&#xff0c;push到远程主机上来执行。pull模式 p…