Redisson分布式锁,重试锁和锁续命的原理

news/2024/5/17 11:58:21

RedissonLock 锁重试原理

tryLock有三个三个参数,第一个是等待时间,第二个是锁失效后自动释放的时间,不填默认为-1,第三个是时间单位;
当设置了第一个参数,那这个锁就成了可重试锁;获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。
获取当前时间,线程ID,尝试获取锁,判断锁失效后自动释放的时间是否等于-1,如果不等于,就用自己的锁释放时间,如果等于-1,异步调用tryLockInnerAsync,返回值是个Future,第一个参数是等待时间,第二个参数是锁释放时间,看门狗的默认30秒,第三个是时间单位,第四个参数是线程ID,这个方法内是个lua脚本,成功返回空,是否返回过期时间;判断返回值是否为空,不为空,计算剩余等待时间,判断等待时间>0,大于就去尝试获取锁,但不是立即获取锁,是在剩余等待时间内订阅了锁释放的情况(锁释放的时候会发布通知),返回值也是个Future,如果超时了,会取消订阅;如果锁已经释放了,计算剩余等待时间,判断剩余等待时间>0,开始循环,就可以重新获取锁,和上面一样,如果失败,同时上,但这里用的是信号量。

在这里插入图片描述
lock.tryLock方法

第一个参数(等待时间),如果设置了,获取锁失败后,就不会立即返回了;会在等待内不断重试;如果在等待时间结束后,还没有获取到锁,那就失败了。所以设置后就变成了可重试的锁了。
第二个参数(锁失效后自动释放的时间,不填默认为-1)
第三个参数(时间单位)
源码跟进

在这里插入图片描述

第二个参数没给,默认值-1,跟进去tryLock方法看看

在这里插入图片描述

在这里插入图片描述
跟进tryAcquire方法,尝试获取锁
在这里插入图片描述
跟进tryAcquireAsync方法

//源码private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {	//判断释放锁的时间是否为-1if (leaseTime != -1L) {//用自己的释放锁的时间return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {//如果是-1,设置一个默认的释放锁的时间,30秒(getLockWatchdogTimeout)RFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e == null) {if (ttlRemaining == null) {this.scheduleExpirationRenewal(threadId);}}});return ttlRemainingFuture;}}

在这里插入图片描述
跟进getLockWatchdogTimeout看门狗

显示默认30秒
在这里插入图片描述
返回RedissonLock类,继续跟进tryLockInnerAsync方法

tryLockInnerAsync 异步方法,有没有拿到结果不清楚

在这里插入图片描述

看到有lua脚本

lua脚本
判断锁是否存在
不存在设置锁标识,v+1
设置有效期
存在,判断锁是不是我自己的
是,v+1
设置有效期
成功获取锁,返回空
失败,返回锁的有效期(毫秒)

  <T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {//释放锁的时间,记录到本地一个成员变量里this.internalLockLeaseTime = unit.toMillis(leaseTime);return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, command, "if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)then redis.call('hincrby', KEYS[1], ARGV[2], 1);redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end;return redis.call('pttl', KEYS[1]);", Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));}

一步一步往上返回,返回到RedissonLock类中的tryLock方法的tryAcquire方法,结果有两种,一种是获取锁成功返回值为空,另一种是获取锁失败返回值为锁释放的时间

在这里插入图片描述
继续

在这里插入图片描述

锁续命

当用默认的锁释放时间时,且已经拿到锁,会创建一个 ConcurrentMap来存锁的记录,以锁的名称为k,线程id为V,判断是否第一次来,如果是开启一个延时任务,10秒后执行,执行的就是更新有效期的lua脚本,更新成功后,开始递归,无限续约。

先看锁释放时间是默认值-1的情况
在这里插入图片描述
跟进 scheduleExpirationRenewal 方法
在这里插入图片描述
跟进 renewExpiration 方法
在这里插入图片描述
跟进 renewExpirationAsync 方法

判断锁是不是当前线程拿到的,是就重置有效期

   protected RFuture<Boolean> renewExpirationAsync(long threadId) {return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)then redis.call('pexpire', KEYS[1], ARGV[1]); return 1; end; return 0;",Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));}

执行lua脚本后
在这里插入图片描述

释放锁

在这里插入图片描述
在这里插入图片描述


http://www.mrgr.cn/p/22438454

相关文章

Rust Web开发实战:打造高效稳定的服务端应用

Rust Web开发实战&#xff1a;打造高效稳定的服务端应用 本书将带领您从零开始构建Web应用程序&#xff0c;无论是API、微服务还是单体应用&#xff0c;都将一一涵盖。您将学到如何优雅地对外开放API&#xff0c;如何连接数据库以安全存储数据&#xff0c;以及如何对应用程序进…

cpp字符串相关

字符串相关 文章参考: [详解-字符串] C++必知必会 字符串-string常用各种操作解析 - 知乎 (zhihu.com) C++ 字符串(string)常用操作总结 - 知乎 (zhihu.com) c++读取字符串和字符的6种函数_c++获取字符串的每个字符-CSDN博客 头文件 #include <string>定义字符串 stri…

持续更新|UNIAPP适配APP遇到的问题以及解决方案

在使用UNIAPP开发APP的时候遇到的一些奇奇怪怪问题记录 组件样式丢失 问题&#xff1a;组件引入界面中&#xff0c;在小程序和H5环境下样式正常&#xff0c;而在APP中却出现高度异常问题 解决&#xff1a;增加view标签将组件包裹起来即可正常显示 解决前&#xff1a; 解决后…

电能表的广泛应用

电能表是一种用来测量电力消费的设备&#xff0c;它在现代社会中得到了广泛的应用。电能表不仅对于居民家庭来说是必不可少的设备&#xff0c;也在工业生产、商业用电等领域发挥着重要的作用。本文将从电能表的发展历史、工作原理、应用领域以及未来发展趋势等方面来探讨电能表…

debian配置四叶草输入法

效果展示 一、前言 在linux下体验比较好的输入法只有两款&#xff1a;搜狗输入法、四叶草输入法。 ubuntu下可以成功配置搜狗输入法&#xff0c;但debian下从来没有成功过。 今天在用fcitx5 四叶草时发现VNC远程输入法会失灵&#xff0c;于是改用了ibus 四叶草&#xff0c…

Windows 10 使用 Vagrant 快速创建虚拟机

一、下载 VirtualBox 官网地址&#xff1a;Oracle VM VirtualBox 阿里云盘&#xff1a;阿里云盘分享 二、安装 VirtualBox 安装软件前请先确认 CPU 是否开启了虚拟化&#xff0c;要求开启 2.1、双击运行可执行文件后点击下一步 2.2、选择安装路径&#xff0c;为了避免中文乱码…

C语言——栈的实现

栈&#xff08;Stack&#xff09;是一种基于先进后出&#xff08;LIFO&#xff09;原则的数据结构&#xff0c;类似于我们平常堆放书籍或者盘子的方式。 栈通常是从高地址向低地址增长的&#xff0c;也就是说&#xff0c;栈顶位于较高的内存地址&#xff0c;而栈底位于较低的内…

响应式动漫音乐/个人博客杂志主题国漫FM模板

国漫FM主题V1.8是以Ajax加以CSS动画的方式,很好的将优雅的设计感和极度精简的代码同时表现了出来,进而缔造出这样一款十分经典的名为Always for you的WordPress博客主题。正如作者自己所言:如果你想让你的WordPress博客看起来个性十足。FM主题为响应式格子布局(瀑布流),是一…

WordPress CVE-2022-4230复现分析

前言 开始CVE审计之旅 WP Statistics WordPress 插件13.2.9之前的版本不会转义参数,这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下,具有管理选项功能 (admin+) 的用户可以使用受影响的功能,但是该插件有一个设置允许低权限用户也可以访问它,其实就是没对admi…

Midjourney之绘画背景的选择

hello 小伙伴们&#xff0c;我是你们的老朋友——树下&#xff0c;今天分享Midjourney提示词中绘画背景的选择&#xff0c;话不多说&#xff0c;直接开始~ 对于背景的选择&#xff0c;Midjourney中主要体现在年代和所处的环境对绘画产生不同的影响 科技的发展&#xff0c;我们…

光影看图 v4.5.6.32 绿色版

更新流水:2024.04.30:跟进官方 4.5.6.32 , 第一版 修改内容:by.星罗月兔&DxFans去校验(方案来自@星罗月兔);去更新;去多余组件及无用菜单!下载地址:https://down.neoimaging.cn/neoimaging/NeoViewerSetup_10000_4.5.6.32.exe本文来自博客园,作者:萌面蛋饺,转载请注明…

保存钉钉群直播回放下载:直播回放下载步骤详解

今天&#xff0c;我们就来拨开云雾&#xff0c;揭开保存钉钉群直播回放的神秘面纱。教会你们如何下载钉钉群直播回放 首先用到的工具我全部打包好了&#xff0c;有需要的自己下载一下 钉钉群直播回放工具下载&#xff1a;https://pan.baidu.com/s/1WVMNGoKcTwR_NDpvFP2O2A?p…

js逆向实战之某证信Accept-Enckey参数加密解析

url:https://webapi.cninfo.com.cn/#/marketDataDate 分析过程抓包,主要关注图中标记的数据包,它的回显数据是我们所需要的。但在该数据包的请求中有一个Accept-Enckey参数是经过加密的,需要知道其加密的逻辑。全局搜索sysapi/p_sysapi1007,只有一处符合的。找到对应地方,…

vite vue-cli 读取文件原始内容 使用base64内容的文件

新建文件 img.dataurl 中存放图片base64 <img class=img :src=dataurl alt= /> import dataurl from ./img.dataurl data(){ return { dataurl:dataurl } } vue.config.js module.exports=defineConfig({ configureWebpack:{module:{rules:[{test:/\.dataurl$/,loader…

Linux进程——子进程的创建(fork的原理)

前言&#xff1a;在上一篇文章中&#xff0c;我们已经会使用getpid/getppid函数来查看pid和ppid,本篇文章会介绍第二种查看进程的方法&#xff0c;以及如何创建子进程&#xff01; 本篇主要内容&#xff1a; 查看进程的第二种方法创建子进程系统调用函数fork 在开始前&#xff…

IoTDB 入门教程③——基于Linux系统快速安装启动和上手

文章目录 一、前文二、下载三、解压四、上传五、启动六、执行七、停止八、参考 一、前文 IoTDB入门教程——导读 二、下载 下载二进制可运行程序&#xff1a;https://dlcdn.apache.org/iotdb/1.3.1/apache-iotdb-1.3.1-all-bin.zip 历史版本下载&#xff1a;https://archive.…

基于 Redis 发布订阅实现服务注册与发现

写在前面 其实很少有公司会使用 Redis 来实现服务注册与发现&#xff0c;通常是ETCD、NACOS、ZOOKEEPER等等&#xff0c;但是也不妨碍我们了解。本文会先介绍 Redis 的发布/订阅模式&#xff0c;接着基于这个模式实现服务注册与发现。 Redis发布订阅流程图&#xff1a; Red…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6.4--汇编LED驱动程序

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

【MATLAB画图】如何绘制图像坐标系

首先我们需要图像坐标轴的原点在左上角&#xff1a; set(gca,ydir,reverse,xaxislocation,top); 然后我们需要坐标轴上加上箭头 quiver(0, 0, 0, 520); % 在(x1, y1)处绘制一个箭头&#xff0c;其方向和长度由(dx, dy)确定 quiver(0, 0, 700, 0); % 在(x1, y1)处绘制一个箭头…

嵌入式全栈开发学习笔记---C语言笔试复习大全3

目录 笔试题3 笔试题4 笔试题5 上一篇介绍了数据类型的长度和数据范围&#xff0c;并且分别讲解了两个经典的笔试题&#xff0c;这一篇我们再来看三道非常经典的考数据类型长度、数据范围和数据类型转换的笔试题。 说明&#xff1a;我们学过单片机的一般都是有C语言基础的了…