Redisson的看门狗策略——保障Redis数据安全与稳定的机制
前言
自定义redis分布式锁无法自动续期,比如,一个锁设置了1分钟超时释放,如果拿到这个锁的线程在一分钟内没有执行完毕,那么这个锁就会被其他线程拿到,可能会导致严重的线上问题,在秒杀场景下,很容易因为这个缺陷导致的超卖了。
在分布式系统中,Redis作为一种高性能、低延迟的内存数据存储系统,被广泛应用于各种场景。然而,在复杂的环境中,Redis数据可能会面临过期失效或死锁等问题,这对应用程序的稳定性和安全性构成了威胁。为了解决这些问题,Redisson库提供了看门狗(Watch Dog)策略。在分布式锁失效好会自动续期 ,在reddison在为定义 leaseTime情况下,开启的时候默认会开启看门狗机制,默认是30s
什么是看门狗策略
看门狗策略是一种自动检测并处理过期键的机制。它基于Redis的“WATCH”命令实现,通过在Redisson库中创建一个监视器(Watch Dog)来监控Redis服务器上的指定键。
当应用程序使用Redisson库监视一个键时,Watch Dog会向Redis服务器发送一个“WATCH”命令,并在服务器上对该键进行监视。如果另一个客户端尝试修改被监视的键,Redis服务器将返回一个错误。这个错误会被Watch Dog捕获并处理。Watch Dog会在Redisson库内部触发一个事件,并将事件传递给应用程序,以便应用程序可以采取相应的操作。
看门狗机制是Redission提供的一种自动延期机制,这个机制使得Redission提供的分布式锁是可以自动续期的。
private long lockWatchdogTimeout = 30 * 1000;
看门狗机制提供的默认超时时间是30*1000毫秒,也就是30秒。如果一个线程获取锁后,运行程序到释放锁所花费的时间大于锁自动释放时间(也就是看门狗机制提供的超时时间30s),那么Redission会自动给redis中的目标锁延长超时时间。在Redission中想要启动看门狗机制,那么我们就不用获取锁的时候自己定义leaseTime(锁自动释放时间)。如果自己定义了锁自动释放时间的话,无论是通过lock还是tryLock方法,都无法启用看门狗机制。但是,如果传入的leaseTime为-1,也是会开启看门狗机制的。
分布式锁是不能设置永不过期的,这是为了避免在分布式的情况下,一个节点获取锁之后宕机从而出现死锁的情况,所以需要个分布式锁设置一个过期时间。但是这样会导致一个线程拿到锁后,在锁的过期时间到达的时候程序还没运行完,导致锁超时释放了,那么其他线程就能获取锁进来,从而出现问题。所以,看门狗机制的自动续期,就很好地解决了这一个问题。
源码解读
进入tryLock方法,这里的tryLock(waitTime, -1, unit)有三个参数
- waitTime:获取锁的最大等待时间(没有传默认为-1)
- leaseTime:锁自动释放的时间(没有传的话默认-1)
- unit:时间的单位(等待时间和锁自动释放的时间单位)
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {return tryLock(waitTime, -1, unit);
}
@Overridepublic boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long time = unit.toMillis(waitTime);long current = System.currentTimeMillis();long threadId = Thread.currentThread().getId();Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return true;}time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}current = System.currentTimeMillis();RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {unsubscribe(subscribeFuture, threadId);}});}acquireFailed(waitTime, unit, threadId);return false;}try {time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}while (true) {long currentTime = System.currentTimeMillis();ttl = tryAcquire(waitTime, leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return true;}time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}// waiting for messagecurrentTime = System.currentTimeMillis();if (ttl >= 0 && ttl < time) {subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}}} finally {unsubscribe(subscribeFuture, threadId);}
// return get(tryLockAsync(waitTime, leaseTime, unit));}
看门狗策略的优点:
1.实时监控:
看门狗能够实时监控Redis服务器上的键,确保数据的实时性和准确性。
2.防止过期失效:
通过定期检测并处理过期键,看门狗策略可防止因键过期而导致的数据失效问题。
3.防止死锁:
在分布式高并发的条件下,如果一个线程获得了锁,但还没有来得及释放锁,就因为系统故障或其它原因使它无法执行释放锁的命令,这会导致其它线程都无法获得锁,从而造成死锁。通过使用看门狗策略,应用程序可以确保所有实例都能及时地响应数据变化,并避免这种情况的发生。
4.可扩展性:
看门狗策略支持横向扩展,能够随着系统的规模增长而保持高性能和稳定性。
watch dog缺点
redisson看门狗虽然能保证在线程没有执行完毕时,锁不会释放,对于秒杀这种强一致性的场景是适用的,但是对于防重这种场景是不适用的,在高并发情况下,会导致接口性能下降。
高并发防重时,如果加锁失败就快速失败,这时候可以使用自定义锁,或者tryLock,如下
RLock lock = redissonClient.getLock("Export:create:" + Context.get().getCorpId());
try {//尝试加锁,最多等待0秒,上锁以后5秒自动解锁if (lock.tryLock(0, 5, TimeUnit.SECONDS)) {//业务处理} else {Assert.isTrue(false, "排队中,请稍后重试!");}
} catch (InterruptedException e) {Assert.isTrue(false, "请勿重复操作!");
} finally {if (lock.isLocked()) {lock.unlock();}
}
Redisson 加解锁API
public void test() throws Exception{RLock lock = redissonClient.getLock("guodong"); // 拿锁失败时会不停的重试// 具有Watch Dog 自动延期机制 默认续30s 每隔30/3=10 秒续到30slock.lock();// 具有Watch Dog 自动延期机制 默认续30s// 尝试拿锁10s后停止重试,返回false 具有Watch Dog 自动延期机制 默认续30sboolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 没有Watch Dog // 尝试获取锁10秒,如果获取不到则放弃lock.lock(10, TimeUnit.SECONDS);// 没有Watch Dog // 尝试获取锁,等待100秒,持有锁10秒钟boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);Thread.sleep(40000L);lock.unlock();}
lock() 方法是阻塞获取锁的方式,如果当前锁被其他线程持有,则当前线程会一直阻塞等待获取锁,直到获取到锁或者发生超时或中断等情况才会结束等待。该方法获取到锁之后可以保证线程对共享资源的访问是互斥的,适用于需要确保共享资源只能被一个线程访问的场景。Redisson 的 lock() 方法支持可重入锁和公平锁等特性,可以更好地满足多线程并发访问的需求。
而 tryLock() 方法是一种非阻塞获取锁的方式,在尝试获取锁时不会阻塞当前线程,而是立即返回获取锁的结果,如果获取成功则返回 true,否则返回 false。Redisson 的 tryLock() 方法支持加锁时间限制、等待时间限制以及可重入等特性,可以更好地控制获取锁的过程和等待时间,避免程序出现长时间无法响应等问题。
默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout来另行指定。另外Redisson 还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。
总结
在使用Redis实现分布式锁的时候,会存在很多问题。
比如说业务逻辑处理时间>自己设置的锁自动释放时间的话,Redis就会按超时情况把锁释放掉,而其他线程就会趁虚而入抢夺锁从而出现问题,因此需要有一个续期的操作。并且,如果释放锁的操作在finally完成,需要判断一下当前锁是否是属于自己的锁,防止释放掉其他线程的锁,这样释放锁的操作就不是原子性了,而这个问题很好解决,使用lua脚本即可。
Redisson的出现,其中的看门狗机制很好解决续期的问题,它的主要步骤如下:
- 在获取锁的时候,不能指定leaseTime或者只能将leaseTime设置为-1,这样才能开启看门狗机制。
- 在tryLockInnerAsync方法里尝试获取锁,如果获取锁成功调用scheduleExpirationRenewal执行看门狗机制
- 在scheduleExpirationRenewal中比较重要的方法就是renewExpiration,当线程第一次获取到锁(也就是不是重入的情况),那么就会调用renewExpiration方法开启看门狗机制。
- 在renewExpiration会为当前锁添加一个延迟任务task,这个延迟任务会在10s后执行,执行的任务就是将锁的有效期刷新为30s(这是看门狗机制的默认锁释放时间)
- 并且在任务最后还会继续递归调用renewExpiration。
也就是总的流程就是,首先获取到锁(这个锁30s后自动释放),然后对锁设置一个延迟任务(10s后执行),延迟任务给锁的释放时间刷新为30s,并且还为锁再设置一个相同的延迟任务(10s后执行),这样就达到了如果一直不释放锁(程序没有执行完)的话,看门狗机制会每10s将锁的自动释放时间刷新为30s。
而当程序出现异常,那么看门狗机制就不会继续递归调用renewExpiration,这样锁会在30s后自动释放。或者,在程序主动释放锁后,流程如下:
- 将锁对应的线程ID移除
- 接着从锁中获取出延迟任务,将延迟任务取消
- 在将这把锁从EXPIRATION_RENEWAL_MAP中移除。
相关文章:
Redisson的看门狗策略——保障Redis数据安全与稳定的机制
前言 自定义redis分布式锁无法自动续期,比如,一个锁设置了1分钟超时释放,如果拿到这个锁的线程在一分钟内没有执行完毕,那么这个锁就会被其他线程拿到,可能会导致严重的线上问题,在秒杀场景下,…...
2.2 消元法的概念
一、消元法介绍 消元法(elimination)是一个求解线性方程组的系统性方法。下面是使用消元法求解一个 2 2 2\times2 22 线性方程组的例子。消元之前,两个方程都有 x x x 和 y y y,消元后,第一个未知数 x x x 将从第…...
删除有序数组中的重复项
目录 题目: 示例: 题目分析: 解题思路: 题目: 给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的…...
【数据库】
文章目录 1. 聚合函数练习: 2. 子查询 1. 聚合函数 where中过滤条件中不能写聚合函数,有聚合函数需要写到Having中 方式一效率高: Select执行流程 练习: 2. 第七题:count(*)有问题,原因是左外连接后…...
高级深入--day38
阳光热线问政平台 http://wz.sun0769.com/index.php/question/questionType?type4 爬取投诉帖子的编号、帖子的url、帖子的标题,和帖子里的内容。 items.py import scrapyclass DongguanItem(scrapy.Item):# 每个帖子的标题title scrapy.Field()# 每个帖子的编…...
基于springboot,vue校园社团管理系统
开发工具:IDEA 服务器:Tomcat9.0, jdk1.8 项目构建:maven 数据库:mysql5.7 系统分前后台,项目采用前后端分离 前端技术:vueelementUI 服务端技术:springbootmybatis-plus 本系…...
广州华锐互动:VR虚拟现实物理学习平台,开启数字化教学新格局
随着虚拟现实(VR)技术的不断发展,越来越多的领域开始应用这一技术。广州华锐互动开发的VR虚拟现实物理学习平台就得到了广泛应用,平台涉及力学、光学、热学等初中物理知识,还包含了物理名人、实验器具、物理现象的还原和学习,相比…...
【tio-websocket】8、T-IO对半包和粘包的处理
介绍 t-io对数据的解码是在DecodeRunnable中完成的,一个TCP连接对应一个DecodeRunnable半包粘包的处理也都在DecodeRunnable中完成的关于DecodeRunnable 先贴上 DecodeRunnable 的源代码: import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import j…...
【Linux】安装与配置虚拟机及虚拟机服务器坏境配置与连接
目录 操作系统介绍 什么是操作系统 常见操作系统 UNIX操作系统 linux操作系统 mac操作系统 嵌入式操作系统 个人版本和服务器版本的区别 安装VMWare虚拟机 VMWare虚拟网卡 编辑 配置虚拟网络编辑器 编辑 安装配置Windows Server 2012 R2 安装Windows Server 2…...
Redis常识
文章目录 缓存的三个风险数据结构淘汰策略 和 过期删除策略过期删除淘汰 如何理解单线程redis特性复制gossip协议事务(和mysql不同,是不严格的事务 )集群(高可用)管道持久化 缓存的三个风险 缓存雪崩(缓存…...
Instant,LocalDate,LocalTime,LocalDateTime和ZonedDateTime
Instant 封装了从 1970-01-01T00:00:00Z 开始的秒数,相当于时间戳。 主要有两个属性: private final long seconds; private final int nanos;LocalDate 用于表示日期,包括年、月、日,例如 2017-12-03。 主要有三个属性&…...
Web入门笔记
Web入门笔记 HTTP协议 超文本传输协议 规定了浏览器和服务器之间数据传输的规则,请问数据和响应数据的格式 基于TCP请求-响应模式一次请求对应一次响应无状态的协议 请问数据格式 浏览器版本:解决浏览器兼容问题。GET请求体:存放请求参数…...
Linux网络编程二(TCP三次握手、四次挥手、TCP滑动窗口、MSS、TCP状态转换、多进程/多线程服务器实现)
TCP三次握手 TCP三次握手(TCP three-way handshake)是TCP协议建立可靠连接的过程,确保客户端和服务器之间可以进行可靠的通信。下面是TCP三次握手的详细过程: 假设客户端为A,服务器为B 1、第一次握手(SYN1,seq500&…...
C#核心笔记——(一)C#和.NET Framework
C#是一种通用的,类型安全的面向对象编程语言。其目标是提高程序员生产力。 一.面向对象 C#实现了丰富的面向对象范式,包括封装、继承、多态。 C#面向对象特性包括: 统一的类型系统 类与接口 属性、方法、事件 C#支持纯函数模式 二、类型安…...
【2023年冬季】华为OD统一考试(B卷)题库清单(已收录345题),又快又全的 B 卷题库大整理
目录 专栏导读华为OD机试算法题太多了,知识点繁杂,如何刷题更有效率呢? 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、滑动窗口5、二叉树6、并查集7、栈 三、算法1、基础算法① 贪心算法② 二分查找③ 分治递归…...
云服务器的先驱,亚马逊云科技海外云服务器领军者
随着第三次工业革命的发展,移动互联网技术带来的信息技术革命为我们的生活带来了极大的便捷。其中,不少优秀的云服务器产品发挥了不可低估的作用,你或许听说过亚马逊云科技、谷歌GCP、IBM Cloud等优秀的海外云服务器。那么云服务器有哪些&…...
QT webengine显示HTML简单示例
文章目录 参考示例1TestWebenqine.promainwindow.hmainwindow.cppmain.cpp效果 示例2 (使用setDevToolsPage函数)main.cpp效果 参考 QT webengine显示HTML简单示例 示例1 编译器 : Desktop Qt 5.15.2 MSVC2019 64bit编辑器: QtCreator代码: TestWebenqine.pro # TestWeben…...
Spark_SQL函数定义(定义UDF函数、使用窗口函数)
一、UDF函数定义 (1)函数定义 (2)Spark支持定义函数 (3)定义UDF函数 (4)定义返回Array类型的UDF (5)定义返回字典类型的UDF 二、窗口函数 (1&…...
【Leetcode】【每日一题】【中等】274. H 指数
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/h-index/description/?envTyped…...
MySQL读写分离技术及实现方案
MySQL读写分离技术及实现方案 本文主要介绍了MySQL读写分离技术的原理、实现方案以及示例。通过使用读写分离技术,可以提高数据库的性能,降低服务器的压力。 一、MySQL读写分离技术简介 读写分离是指将数据库的读操作和写操作分别分配到不同的服务器上…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
