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

news2024/11/20 11:39:15

文章目录

    • 一、简介
    • 二、集成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.01
    rBloomFilter.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 是一个全局的静态常量Map
    RedissonBaseLock.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.coloradmin.cn/o/2199748.html

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

相关文章

基于java+springboot的旅游信息网站、旅游景区门票管理系统设计与实现

该系统是基于javaspringboot开发的旅游景区门票管理系统。是给师弟开发的大四实习作品。学习过程中&#xff0c;遇到问题可以咨询github作者。 演示地址 前台地址&#xff1a; http://travel.gitapp.cn 后台地址&#xff1a; http://travel.gitapp.cn/admin 后台管理帐号&am…

开发一个ftp上传客户端

文章目录 需求分析Tkinter基本用法多窗口切换FTP上传 程序打包源码 需求 项目中有个小功能模块 &#xff0c;需要win下实现ftp上传功能&#xff0c;编写一个DEMO测试 要求 界面简单选择本地文件 上传ftp服务器显示进度条显示状态上传完成后显示URL分享地址 分析 Tkinter Tkint…

【读书笔记·VLSI电路设计方法解密】问题6:超大规模集成电路(VLSI)设计实现的主要方法是什么

现代芯片设计实践的主要方法包括: 定制设计现场可编程门阵列 (FPGA)基于标准单元的设计 (ASIC)平台/结构化ASIC在定制设计方法中,每个晶体管都是手动设计和布局的。这种方法的主要优势在于电路可以高度优化以提高速度、减少面积或降低功耗。然而,由于涉及大量手工工作,这种…

什么是物联网nb水表?

物联网NB水表是一种利用NB-IoT(窄带物联网)技术实现远程数据传输的智能水表。这种水表不仅能够精确计量用户的用水量&#xff0c;还能通过无线通信技术实现数据的远程传输和管理。下面我们来详细介绍物联网NB水表的主要特点和功能。 一、基本概念 -定义&#xff1a;物联网NB水…

nVisual集成项目交付模式升级方案

集 成 项 目 的 普 遍 现 状 1 集成项目的普遍现状 设计、工程和运维各部门使用不同的软件工具&#xff0c;缺乏有效的协同&#xff0c;工程数据无法有效积累并转化为运维数据&#xff1b; 传统项目验收交付模式已经无法满足用户的需求&#xff0c;需要项目交付后协助用…

分治算法(6)_归并排序_交易逆序对的总数

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 分治算法(6)_归并排序_交易逆序对的总数 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48…

怎么去掉图片上的文字不留痕迹?学会这5种P图方法轻松解决

图片编辑已成为我们日常生活和工作中不可或缺的一部分。但有时候&#xff0c;图片上的一些文字却成了我们分享或使用的障碍。如何无痕去除图片上的文字呢&#xff1f;今天&#xff0c;我将为大家介绍5种高效工具&#xff0c;让你轻松P图&#xff0c;一起来学习下吧。 工具一&am…

ESP32利用WebServer进行设备配置

目标需求 利用esp32的WebServer功能&#xff0c;展示一个网页&#xff0c;对里面的参数进行配置&#xff0c;并以json文本格式保存到flash里面。 1、定义HTML const char index_html[] PROGMEM R"rawliteral( <!DOCTYPE html> <html lang"en"> …

前沿论文 M5Product 组会 PPT

对比学习&#xff08;Contrast learning&#xff09;&#xff1a;对比学习是一种自监督学习方法&#xff0c;用于在没有标签的情况下&#xff0c;通过让模型学习哪些数据点相似或不同来学习数据集的一般特征。假设一个试图理解世界的新生婴儿。在家里&#xff0c;假设有两只猫和…

PPT在线画SWOT分析图!这2个在线软件堪称办公必备!

swot分析ppt怎么做&#xff1f; swot分析是一个非常常用的战略分析框架&#xff0c;经常会在ppt中使用。想在ppt中绘制swot分析图&#xff0c;使用自带的形状工具可以制作出来&#xff0c;但绘制效率不够高&#xff0c;在需要大批量制作的场景下&#xff0c;会让人非常心累………

【WebGis开发 - Cesium】三维可视化项目教程---初始化场景

系列文章目录 【WebGis开发 - Cesium】三维可视化项目教程—视点管理 目录 系列文章目录引言一、Cesium引入项目1.1 下载资源1.2 项目引入Cesium 二、初始化地球2.1 创建基础文件2.1.1 创建Cesium工具方法文件2.1.2 创建主页面 2.2 看下效果 三、总结 引言 本教程主要是围绕Ce…

现场直击!2023望繁信科技产品发布会精彩回顾

2023望繁信科技产品发布会圆满结束。 感谢200余名企业代表、合作伙伴、媒体到场参会&#xff0c;感谢3万多位关注望繁信科技和流程挖掘的朋友在线观看直播。 在会上&#xff0c;我们正式分享了望繁信科技多年深耕流程挖掘领域的思考、积累和部署&#xff0c;发布了过去一年在…

Pyppeteer:如何在 Python 中使用 Puppeteer 和 Browserless?

Python 中的 Pyppeteer 是什么&#xff1f; Pyppeteer 是流行的 Node.js 库 Puppeteer 的 Python 移植版本&#xff0c;用于以编程方式控制无头 Chrome 或 Chromium 浏览器。 本质上&#xff0c;Pyppeteer 允许 Python 开发人员在 Web 浏览器中自动执行任务&#xff0c;例如抓…

webm格式怎么转换成mp4?值得给你推荐的几种简单方法

webm格式怎么转换成mp4&#xff1f;MP4支持多种音频和视频编解码器&#xff0c;如H.264和AAC&#xff0c;用户可以根据需要调整视频和音频质量&#xff0c;以满足不同需求。同时&#xff0c;许多视频编辑软件广泛支持MP4格式&#xff0c;使得剪辑、合成和特效处理变得更加便捷。…

人工智能、人机交互和机器人国际学术会议

第三届人工智能、人机交互和机器人国际学术会议 &#xff08;AIHCIR 2024&#xff09;组委会热忱地邀请您参与本届大会。本届大会旨在聚集领先的科学家、研究人员和学者&#xff0c;共同交流和分享在人工智能、人机交互和机器人各个方面的经验和研究成果&#xff0c;为研究人员…

【C++】模板(初识):函数模板、类模板

本篇主要介绍C中的模板初阶的一些知识。模板分为函数模板和类模板&#xff0c;我们一个一个来看。 1.函数模板 1.1函数模板概念 函数模板代表了一个函数家族&#xff0c;该函数模板与类型无关&#xff0c;在使用时被参数化&#xff0c;根据实际的参数类型产生函数特定版本。…

LSTM时间序列模型实战——预测上证指数走势

LSTM时间序列模型实战——预测上证指数走势 关于作者 作者&#xff1a;小白熊 作者简介&#xff1a;精通python、matlab、c#语言&#xff0c;擅长机器学习&#xff0c;深度学习&#xff0c;机器视觉&#xff0c;目标检测&#xff0c;图像分类&#xff0c;姿态识别&#xff0c;…

五款软件助你秒变职场达人

✨ 每天忙碌于工作&#xff0c;却感觉事半功倍&#xff1f;别担心&#xff0c;今天就为大家揭秘5款高效工作软件&#xff0c;让你秒变职场达人&#xff0c;效率直线飙升&#xff01;&#x1f389; 1️⃣ 亿可达 &#x1f3d7;️ 软件连接神器 &#x1f3af; 特点&#xff1a…

Java后端面试很水的,7天就能搞定!

随着Java的越来越卷&#xff0c;面试也直接上难度了&#xff0c;从以前的八股文到场景题了&#xff0c;尤其是有经验的去面试&#xff0c;场景题都是会问的&#xff0c;近期面试过的应该都深有体会&#xff01; 场景题230道&#xff1a; 1.分布式锁加锁失败后的等待逻辑是如何…

项目管理全流程包括哪些环节,一文读懂项目管理全流程

项目管理全流程是一个系统性、阶段性的过程&#xff0c;旨在确保项目从启动到完成的高效运行。该流程包括以下几个关键阶段&#xff1a;项目管理全流程是确保项目从启动到完成顺利进行的一系列阶段和活动的总称。 以下是基于项目管理全流程对项目管理的描述&#xff1a; 一、…