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

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);}
}

相关文章:

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

文章目录 一、简介二、集成redissionpom文件redission 配置文件application.yml文件启动类 三、JAVA 操作案例字符串操作哈希操作列表操作集合操作有序集合操作布隆过滤器操作分布式锁操作 四、源码解析 一、简介 Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格…...

npm包管理深度探索:从基础到进阶全面教程!

目录 一、npm概述1、npm简介&#xff08;1&#xff09;什么是npm&#xff1f;&#xff08;2&#xff09;npm的核心功能&#xff08;3&#xff09;npm的工作原理&#xff08;4&#xff09;npm的优势&#xff08;5&#xff09;npm的局限性&#xff08;6&#xff09;总结 2、npm的…...

最新免费GPT4O和Midjourney

一、什么是GPT4O&#xff1f; GPT-4 是 OpenAI 研发的大型语言模型。它具有强大的语言理解和生成能力&#xff0c;在自然语言处理等诸多领域有着广泛的应用和表现。 二、什么是Midjourney&#xff1f; Midjourney 是一款人工智能图像生成工具。它可以根据用户输入的描述或提…...

python操作OpenAI教程

python操作OpenAI pip install -U openai代码&#xff1a; from openai import OpenAI# 解决请求超时问题 import os os.environ["http_proxy"] "http://localhost:7890" os.environ["https_proxy"] "http://localhost:7890"# 需要…...

如何版本REST API:综合指南

目录 总则什么是REST API版本控制?为什么API版本控制很重要?如何对REST API进行版本控制 理解API契约评估需求选择版本控制策略沟通变化保持向后兼容性详细文档记录REST API版本控制最佳实践REST API版本控制常见问题:REST API版本控制总则 版本化REST API对于确保软件应用…...

Docker 环境下 Nginx 监控实战:使用 Prometheus 实现 Nginx 性能监控的完整部署指南

Docker 环境下 Nginx 监控实战&#xff1a;使用 Prometheus 实现 Nginx 性能监控的完整部署指南 文章目录 Docker 环境下 Nginx 监控实战&#xff1a;使用 Prometheus 实现 Nginx 性能监控的完整部署指南一 查看模块是否安装二 配置 status 访问端点三 Docker 部署 nginx-prome…...

网络安全-IPv4和IPv6的区别

1. 2409:8c20:6:1135:0:ff:b027:210d。 这是一个IPv6地址。IPv6&#xff08;互联网协议版本6&#xff09;是用于标识网络中的设备的一种协议&#xff0c;它可以提供比IPv4更大的地址空间。这个地址由八组十六进制数字组成&#xff0c;每组之间用冒号分隔。IPv6地址通常用于替代…...

【移动端】事件基础

一、移动端事件分类 移动端事件主要分为以下几类&#xff1a; 1. 触摸事件&#xff08;Touch Events&#xff09; 触摸事件是移动设备特有的事件&#xff0c;用来处理用户通过触摸屏幕进行的操作。主要的触摸事件有&#xff1a; touchstart&#xff1a;手指触摸屏幕时触发。…...

软件测试比赛-学习

一、环境配置 二、浏览器适配 //1.设置浏览器的位置,google浏览器位置是默认且固定在电脑里的//2.设置浏览器驱动的位置,C:\Users\27743\AppData\Local\Google\Chrome\ApplicationSystem.setProperty("webdriver.chrome.driver", "C:\\Users\\27743\\AppData\\…...

力扣LeetCode-链表中的循环与递归使用

标题做题的时候发现循环与递归的使用差别&#xff1a; 看两道题&#xff1a; 两道题都是不知道链表有多长&#xff0c;所以需要用到循环&#xff0c;用到循环就可以把整个过程分成多个循环体&#xff0c;就是每一次循环要执行的内容。 反转链表&#xff1a; 把null–>1…...

AFSim仿真系统 --- 系统简解_08 传感器与特征

传感器与特征 传感器是平台的一部分&#xff0c;为拥有该平台提供了探测其他平台及其组成部分的能力。 特征是平台的一种属性&#xff0c;用于确定特定传感器是否能够探测到特征所拥有的平台。 以下是用于探测平台的一些特征属性列表&#xff1a; 声学红外光学雷达 AFSIM …...

已经安装了qt,想添加mingw组件,包含gcc等

1、已经安装了qt&#xff0c;想添加mingw组件&#xff0c; 步骤1 双击打开MaintenanceTool.exe&#xff0c; 步骤2&#xff1a; 选择清华大学开源软件镜像网站&#xff0c;选择相应QT版本添加网址https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepository/windows_x8…...

数据库管理-第250期 深入浅出多主多活数据库技术- Cantian存储引擎(一)(20241009)

数据库管理250期 2024-10-09 数据库管理-第250期 深入浅出多主多活数据库技术- Cantian存储引擎&#xff08;一&#xff09;&#xff08;20241009&#xff09;1 简介2 引擎构成3 引擎架构4 文件分布5 分布式MVCC6 限制/要求总结 数据库管理-第250期 深入浅出多主多活数据库技术…...

信号用wire类型还是reg类型定义

wire类型就是一根线&#xff0c;线有两端&#xff0c;一端发生改变&#xff0c;经过线传递的信号当然也会发生改变&#xff0c;reg类型则不同&#xff0c;可以把reg类型理解为存储数据的寄存器&#xff0c;当满足一定条件时&#xff0c;数值才被激活发生改变。 那么&#xff0…...

与鸿蒙同行,Java Solon v3.0 正式发布(向下兼容)

与 HarmonyOS NEXT 10月8日公测同行。Solon &#xff08;开放原子开源基金会&#xff0c;孵化项目&#xff09; v3.0 正式发布&#xff0c; 且将在 2025 年启动华为编程语言 “仓颉” 版开发&#xff08;届时会有 Solon-Java 和 Solon-CJ 两大版本&#xff09;。本次大版本更新…...

数据驱动投资:AI在股票市场的应用

当ChatGPT首次亮相时&#xff0c;其卓越的语言处理能力立刻引起了许多行业的广泛关注&#xff0c;投资界也不例外。关于ChatGPT是否能应用于投资决策的问题&#xff0c;迅速成为热门讨论的焦点。 近期&#xff0c;加拿大多伦多大学和印度孟买理工学院的研究人员联合开展了一项…...

Tita使用小技巧:项目类型自定义,业务管理个性化

应用场景 某互联网企业&#xff0c;企业内部有研发部&#xff0c;有销售部 研发部通常需要管理各种技术研发类型项目&#xff0c;因此需要管理「研发型」项目&#xff0c;该类型项目需要明确「所属产品」、「级别管理&#xff1a;国家级/省市级」、「项目类别&#xff1a;创新…...

国药准字:您购药的可靠指南

益安宁丸为同溢堂药业有限公司所独家生产的一款中成药&#xff0c;在内地市场和港澳地区均有上市。 益安宁丸真品的唯一标准&#xff1a;无论港版还是内地版&#xff0c;包装盒必然有国药准字Z20063087标识&#xff0c;但凡没有国药准字标识的必为假药。...

Python类学习(2)——未完待续

函数和类的参数传递 实例方法 class House:# 类构造方法&#xff0c;也是实例方法def __init__(self, area, price):self.area areaself.price pricedef cls_func(self, arg):print(arg)my_house House(America, 330) # 实例化类对象,需要传入init中的两个参数 print(f…...

微前端框架 qiankun 全面解析到源码实现

微前端&#xff08;Micro Frontends&#xff09;是一种将大型前端应用拆分为多个独立模块的架构设计思想&#xff0c;旨在解决复杂应用开发与维护的难题。qiankun 是目前流行的微前端解决方案之一&#xff0c;基于 single-spa 进行封装&#xff0c;提供了一种简单且完整的微前端…...

大模型时代,小白程序员如何抓住机遇?阿里高薪Offer背后的大模型学习指南(收藏版)

文章主要介绍了阿里在大模型领域的强势发展&#xff0c;包括高薪Offer和招聘趋势&#xff0c;强调了AI技能的重要性。作者建议小白和程序员学习大模型技术&#xff0c;并推荐了“派聪明RAG项目”作为学习资源。同时&#xff0c;文章还探讨了AI工具的实际应用和挑战&#xff0c;…...

白细胞介素-6(IL-6)在临床疾病中的作用机制与靶向治疗研究进展

白细胞介素-6&#xff08;Interleukin-6, IL-6&#xff09;是一种由多种细胞&#xff08;如单核/巨噬细胞、T细胞、成纤维细胞等&#xff09;分泌的多效性细胞因子&#xff0c;参与免疫调节、炎症反应、代谢稳态及组织修复等生理过程。在病理状态下&#xff0c;IL-6过度表达与感…...

如何在5分钟内掌握VSCode Mermaid图表实时预览:开发者终极指南

如何在5分钟内掌握VSCode Mermaid图表实时预览&#xff1a;开发者终极指南 【免费下载链接】vscode-mermaid-preview Previews Mermaid diagrams 项目地址: https://gitcode.com/gh_mirrors/vs/vscode-mermaid-preview 还在为编写技术文档时需要在代码编辑器与图表预览工…...

大模型应用开发:小白程序员必备的收藏指南——Agent开发与算法岗界限全解析

本文探讨了在大模型应用开发、AI应用开发以及Agent开发中&#xff0c;开发和算法岗位的界限模糊问题。通过分析实习生的困惑、HR的挑战以及行业招聘趋势&#xff0c;指出Agent工程化通常需要开发与算法合作。文章还讨论了应用算法工程师是否会消失&#xff0c;认为虽然岗位可能…...

5分钟掌握Cherry MX键帽3D建模:打造你的专属机械键盘

5分钟掌握Cherry MX键帽3D建模&#xff1a;打造你的专属机械键盘 【免费下载链接】cherry-mx-keycaps 3D models of Chery MX keycaps 项目地址: https://gitcode.com/gh_mirrors/ch/cherry-mx-keycaps 你是否曾想过亲手设计一套完全属于自己的机械键盘键帽&#xff1f;…...

Cursor Pro免费激活终极指南:简单快速解锁AI编程高级功能

Cursor Pro免费激活终极指南&#xff1a;简单快速解锁AI编程高级功能 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your …...

前端工程化实战:代码规范、兼容性、调试与项目整合

前言学完 HTML 和 CSS 的核心知识后&#xff0c;如何写出规范、可维护、兼容性好的代码&#xff0c;并高效地调试和构建项目&#xff0c;是很多初学者的薄弱环节。本篇整合 代码书写规范、浏览器兼容性处理、Chrome DevTools 调试技巧、项目目录结构 以及 前端学习路径 等实用技…...

麒麟KylinOS 2303系统管理员必备:用模板为新用户批量配置统一电源策略

麒麟KylinOS 2303系统管理员实战&#xff1a;批量配置用户电源策略的模板化方案 在企业办公环境或学校机房中&#xff0c;麒麟KylinOS系统管理员经常面临统一管理多台电脑电源策略的需求。传统逐台配置的方式效率低下&#xff0c;而通过/etc/skel/用户模板目录的机制&#xff0…...

CentOS 7下VNC连接Sentaurus TCAD服务器,从安装到排错的保姆级避坑指南

CentOS 7下高效连接Sentaurus TCAD的工程实践指南 在半导体设计与仿真领域&#xff0c;Sentaurus TCAD作为行业标准工具链&#xff0c;其服务器环境的稳定访问是研发效率的关键保障。对于刚接触Linux服务器环境的工程师或研究人员而言&#xff0c;如何通过VNC实现图形化界面的远…...

LVGL事件处理实战:从按钮点击到滚动列表,手把手教你写交互代码(附避坑指南)

LVGL事件处理实战&#xff1a;从按钮点击到滚动列表&#xff0c;手把手教你写交互代码&#xff08;附避坑指南&#xff09; 在嵌入式GUI开发中&#xff0c;流畅的交互体验往往决定了产品的成败。LVGL作为轻量级通用图形库&#xff0c;其事件处理机制是构建动态界面的核心。本文…...