[[437124]]
Redis 漫步式锁使用 SET 辅导就不错竣事了么?在漫步式限制 CAP 表面一直存在。
漫步式锁的门谈可没那么浅易,咱们在网上看到的漫步式锁决议可能是有问题的。
「码哥」一步步带你深切漫步式锁是如何一步步完善,在高并发分娩环境中如何正确使用漫步式锁。
在插足正文之前,咱们先带着问题去念念考:
什么时候需要漫步式锁? 加、解锁的代码位置有负责么? 如何幸免出现锁再也无法删除? 超经常间修复些许合适呢? 如何幸免锁被其他线程开释 如何竣事重入锁? 主从架构会带来什么安全问题? 什么是 Redlock Redisson 漫步式锁最好实战 看门狗竣事旨趣 …… 什么时候用漫步式锁?码哥,说个庸碌的例子培植下什么时候需要漫步式锁呢?
诊所只消一个医师,许多患者前来就诊。
医师在吞并本领只可给一个患者提供就诊就业。
如若不是这样的话,就会出现医师在就诊肾亏的「肖菜鸡」准备开药时候患者切换成了脚臭的「谢霸哥」,这时候药就被谢霸哥取走了。
治肾亏的药被有脚臭的拿去了。
当并发去读写一个【分享资源】的时候,咱们为了保证数据的正确,需要放胆吞并本领只消一个线程探听。
漫步式锁即是用来放胆吞并本领,只消一个 JVM 程度中的一个线程不错探听被保护的资源。
漫步式锁初学65 哥:漫步式锁应该餍足哪些特色?
互斥:在职何给定本领,只消一个客户端不错捏有锁;
无死锁:任何本领王人有可能得回锁,即使获取锁的客户端崩溃;
容错:只消大无数 Redis的节点王人照旧启动,客户端就不错获取和开释锁。
码哥,我不错使用 SETNX key value 号召是竣事「互斥」特色。
这个号召来自于SET if Not eXists的缩写,旨趣是:如若 key 不存在,则修复 value 给这个key,不然啥王人不作念。Redis 官方地址说的:
号召的复返值:
1:修复奏凯; 0:key 莫得修复奏凯。如下场景:
敲代码一天累了,想去减弱推拿下肩颈。
168 号技师最抢手,群众心爱点,是以并发量大,需要漫步式锁放胆。
吞并本领只允许一个「客户」预约 168 技师。
肖菜鸡恳求 168 技师奏凯:
> 亚星捕鱼SETNX lock:168 1 (integer) 1 # 获取 168 技师奏凯
谢霸哥背面到,恳求失败:
> SETNX lock 2 (integer) 0 # 客户谢霸哥 2 获取失败
此刻,恳求奏凯的客户就不错享受 168 技师的肩颈减弱就业「分享资源」。
享受收尾后,要实时开释锁,给自后者享受 168 技师的就业契机。
肖菜鸡,码哥考考你如何开释锁呢?
很浅易,使用 DEL 删除这个 key 就行。
> DEL lock:168 (integer) 1
码哥,你见过「龙」么?我见过,因为我被一条龙就业过。
肖菜鸡,事情可没这样浅易。
这个决议存在一个存在变成锁无法开释的问题,变成该问题的场景如下:
客户端地点节点崩溃,无法正确开释锁;
业务逻辑特殊,无法履行 DEL辅导。
这样,这个锁就会一直占用,锁在我手里,我挂了,这样其他客户端再也拿不到这个锁了。
超时修复码哥,我不错在获取锁奏凯的时候修复一个「超经常间」
比如设定推拿就业一次 60 分钟,那么在给这个 key 加锁的时候修复 60 分钟逾期即可:
> SETNX lock:168 1 // 获取锁 (integer) 1 > EXPIRE lock:168 60 // 60s 自动删除 (integer) 1
这样,到点后锁自动开释,其他客户就不错络续享受 168 技师推拿就业了。
谁要这样写,就糟透了。
「加锁」、「修复超时」是两个号召,他们不是原子操作。
如若出现只履行了第一条,第二条没契机履行就会出现「超经常间」修复失败,依然出现锁无法开释。
码哥,那咋办,我想被一条龙就业,要处治这个问题
Redis 2.6.X 之后,官方拓展了 SET 号召的参数,餍足了当 key 不存在则修复 value,同期修复超经常间的语义,况兼餍足原子性。
SET resource_name random_value NX PX 30000NX:暗示只消 resource_name 不存在的时候才气 SET 奏凯,从而保证只消一个客户端不错得回锁; PX 30000:暗示这个锁有一个 30 秒自动逾期期间。
这样写还不够,咱们还要防备不成开释不是我方加的锁。咱们不错在 value 上作念著作。
络续往下看……
开释了不是我方加的锁这样我能适当的享受一条龙就业了么?
No,还有一种场景会导致开释别东谈主的锁:
客户 1 获取锁奏凯并修复修复 30 秒超时;
客户 1 因为一些原因导致履行很慢(收集问题、发生 FullGC……),过了 30 秒依然没履行完,然而锁逾期「自动开释了」;
客户 2 恳求加锁奏凯;
客户 1 履行完成,履行 DEL 开释锁辅导,这个时候就把客户 2 的锁给开释了。
有个重要问题需要处治:我方的锁只可我方来开释。
我要如何删除是我方加的锁呢?
在履行 DEL 辅导的时候,咱们要想主义检查下这个锁是不是我方加的锁再履行删除辅导。
解铃还须系铃东谈主
码哥,我在加锁的时候修复一个「惟一璀璨」看成 value 代表加锁的客户端。SET resource_name random_value NX PX 30000
在开释锁的时候,客户端将我方的「惟一璀璨」与锁上的「璀璨」相比是否至极,匹配上则删除,不然莫得职权开释锁。
伪代码如下:
皇冠客服飞机:@seo3687
// 比对 value 与 惟一璀璨 if (redis.get("lock:168").equals(random_value)){ redis.del("lock:168"); //比对奏凯则删除 }
有莫得想过,这是 GET + DEL 辅导组合而成的,这里又会触及到原子性问题。
咱们不错通过 Lua 剧蓝本竣事,这样判断和删除的过程即是原子操作了。
// 获取锁的 value 与 ARGV[1] 是否匹配,匹配则履行 del if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
这样通过惟一值修复成 value 璀璨加锁的客户端很蹙迫,仅使用 DEL 是不安全的,因为一个客户端可能会删除另一个客户端的锁。
使用上头的剧本,每个锁王人用一个当场字符串“签名”,只消当删除锁的客户端的“签名”与锁的 value 匹配的时候,才会删除它。
官方文档亦然这样说的:https://redis.io/topics/distlock
这个决议照旧相对完好,咱们用的最多的可能即是这个决议了。
正确修复锁超时锁的超经常间如何狡计合适呢?
这个期间不成瞎写,一般要说明在测试环境屡次测试,然后压测多轮之后,比如狡计出平均履行期间 200 ms。
那么锁的超经常间就放大为平均履行期间的 3~5 倍。
皇冠体育备用网址为啥要放放大呢?
因为如若锁的操作逻辑中有收集 IO 操作、JVM FullGC 等,线上的收集不会总一帆风顺,咱们要给收集抖动留有缓冲期间。
那我修复更大小数,比如修复 1 小时不是更安全?
不要钻牛角,多大算大?
修复期间过长,一朝发生宕机重启,就意味着 1 小时内,漫步式锁的就业全部节点不可用。
你要让运维手动删除这个锁么?
只消运维确凿不会打你。
有莫得完好的决议呢?不管期间如何修复王人不大合适。
咱们不错让得回锁的线程开启一个督察线程,用来给将近逾期的锁「续航」。
加锁的时候修复一个逾期期间,同期客户端开启一个「督察线程」,定时去检测这个锁的失效期间。
如若将近逾期,然而业务逻辑还没履行完成,自动对这个锁进行续期,从新修复逾期期间。
这个意念念行得通,可我写不出。
别慌,照旧有一个库把这些职责王人封装好了他叫 Redisson。
在使用漫步式锁时,它就经受了「自动续期」的决议来幸免锁逾期,这个督察线程咱们一般也把它叫作念「看门狗」线程。
一齐优化下来,决议似乎相比「严谨」了,笼统出对应的模子如下。
通过 SET lock_resource_name random_value NX PX expire_time,同期启动督察线程为将近逾期但还没履行完的客户端的锁续命; 客户端履行业务逻辑操作分享资源; 通过 Lua 剧本开释锁,先 get 判断锁是否是我方加的,再履行 DEL。这个决议施行上照旧相比完好,能写到这一步照旧击败 90% 的标准猿了。
然而对于追求极致的标准员来说还远远不够:
可重入锁如何竣事? 主从架构崩溃复原导致锁丢失如何处治? 客户端加锁的位置有门谈么? 加解锁代码位置有负责说明前边的分析,咱们照旧有了一个「相对严谨」的漫步式锁了。
于是「谢霸哥」就写了如下代码将漫步式锁利用到名堂中,以下是伪代码逻辑:
public void doSomething() { redisLock.lock(); // 上锁 try { // 处理业务 ..... redisLock.unlock(); // 开释锁 } catch (Exception e) { e.printStackTrace(); } }
有莫得想过:一朝履行业务逻辑过程中抛出特殊,标准就无法履行开释锁的经由。
是以开释锁的代码一定要放在 finally{} 块中。
加锁的位置也有问题,放在 try 外面的话,如若履行 redisLock.lock() 加锁特殊,然而施行辅导照旧发送到就业端并履行,仅仅客户端读取反馈超时,就会导致莫得契机履行解锁的代码。
如何才能开通皇冠信用盘是以 redisLock.lock() 应该写在 try 代码块,这样保证一定会履行解锁逻辑。
说七说八,正确代码位置如下 :
public void doSomething() { try { // 上锁 redisLock.lock(); // 处理业务 ... } catch (Exception e) { e.printStackTrace(); } finally { // 开释锁 redisLock.unlock(); } }竣事可重入锁
65 哥:可重入锁要如何竣事呢?
当一个线程履行一段代码奏凯获取锁之后,络续履行时,又遭受加锁的代码,可重入性就就保证线程能络续履行,而不可重入即是需要恭候锁开释之后,再次获取锁奏凯,才气络续往下履行。
欧博正网用一段代码讲明注解可重入:
public synchronized void a() { b(); } public synchronized void b() { // pass }
假定 X 线程在 a 法子获取锁之后,络续履行 b 法子,如若此时不可重入,线程就必须恭候锁开释,再次争抢锁。
成就锁明明是被 X 线程领有,却还需要恭候我方开释锁,然后再去抢锁,这看起来就很奇怪,我开释我我方~
Redis Hash 可重入锁Redisson 类库即是通过 Redis Hash 来竣事可重入锁
当线程领有锁之后,往后再遭受加锁法子,径直将加锁次数加 1,然后再履行法子逻辑。
退出加锁法子之后,加锁次数再减 1,当加锁次数为 0 时,锁才被的确的开释。
不错看到可重入锁最大特色即是计数,狡计加锁的次数。
是以当可重入锁需要在漫步式环境竣事时,咱们也就需要统计加锁次数。
加锁逻辑咱们不错使用 Redis hash 结构竣事,key 暗示被锁的分享资源, hash 结构的 fieldKey 的 value 则保存加锁的次数。
从国际戏剧公园小程序展示的7月23日节目单可以看出,“只有红楼梦·戏剧幻城”的剧场安排分为主剧场和微剧场。主剧场中,真亦假、有还无、读者剧场每天都安排了两场演出,每场演出时间均超过或接近1个小时。微剧场中,有的节目一天甚至排了四场。
太平洋在线百家乐成都大运会博物馆位于世界大运公园——成都东安湖体育公园主体育场,紧临风景秀丽东安湖,正对大运火炬塔,拥有近百米长的观景平台,展厅面积3300平方米,馆名由中国书法家协会主席孙晓云女士题写,展陈以大运会成功申办、紧张筹办和精彩举办为主线,按时代使命、责任担当和全球盛会三个篇章进行布展。
通过 Lua 剧本竣事原子性,假定 KEYS1 = 「lock」, ARGV「1000,uuid」:
---- 1 代表 true ---- 0 代表 false if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return 1; 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 1; end ; return 0;
加锁代码最初使用 Redis exists 号召判断面前 lock 这个锁是否存在。
如若锁不存在的话,径直使用 hincrby创建一个键为 lock hash 表,况兼为 Hash 表中键为 uuid 启动化为 0,然后再次加 1,临了再修复逾期期间。
如若面前锁存在,欧博开户则使用 hexists判断面前 lock 对应的 hash 表中是否存在 uuid 这个键,如若存在,再次使用 hincrby 加 1,临了再次修复逾期期间。
临了如若上述两个逻辑王人不得当,径直复返。
解锁逻辑-- 判断 hash set 可重入 key 的值是否等于 0
-- 如若为 0 代表 该可重入 key 不存在
-- 判断 hash set 可重入 key 的值是否等于 0 -- 如若为 0 代表 该可重入 key 不存在 if (redis.call('hexists', KEYS[1], ARGV[1]) == 0) then return nil; end ; -- 狡计面前可重入次数 local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1); -- 小于等于 0 代表不错解锁 if (counter > 0) then return 0; else redis.call('del', KEYS[1]); return 1; end ; return nil;
最初使用 hexists 判断 Redis Hash 表是否存给定的域。
如若 lock 对应 Hash 表不存在,或者 Hash 表不存在 uuid 这个 key,径直复返 nil。
若存在的情况下,代表面前锁被其捏有,最初使用 hincrby使可重入次数减 1 ,然后判断狡计之后可重入次数,若小于等于 0,则使用 del 删除这把锁。
解锁代码履行模式与加锁肖似,只不外解锁的履行终结复返类型使用 Long。这里之是以莫得跟加锁相通使用 Boolean ,这是因为解锁 lua 剧本中,三个复返值含义如下:
1 代表解锁奏凯,锁被开释 0 代表可重入次数被减 1 null 代表其他线程尝试解锁,解锁失败. 主从架构带来的问题码哥,到这里漫步式锁「很完好了」吧,没猜测漫步式锁这样多门谈。
路还很远,之前分析的场景王人是,锁在「单个」Redis 实例中可能产生的问题,并莫得触及到 Redis 主从模式导致的问题。
咱们经常使用「Cluster 集群」或者「哨兵集群」的模式部署保证高可用。
这两个模式王人是基于「主从架构数据同步复制」竣事的数据同步,而 Redis 的主从复制默许是异步的。
以下内容来自于官方文档 https://redis.io/topics/distlock
咱们试想下如下场景会发生什么问题:
客户端 A 在 master 节点获取锁奏凯。
还莫得把获取锁的信息同步到 slave 的时候,master 宕机。
slave 被选举为新 master,这时候莫得客户端 A 获取锁的数据。
客户端 B 就能奏凯的得回客户端 A 捏有的锁,破损了漫步式锁界说的互斥。
天然这个概率极低,然而咱们必须得承认这个风险的存在。
Redis 的作家提议了一种处治决议,叫 Redlock(红锁)
Redis 的作家为了协调漫步式锁的尺度,搞了一个 Redlock,算是 Redis 官方对于竣事漫步式锁的指引表率,https://redis.io/topics/distlock,然而这个 Redlock 也被外洋的一些漫步式众人给喷了。
因为它也不完好,有“裂缝”。
什么是 Redlock红锁是不是这个?
皇冠登3代理出租[[437127]]
泡面吃多了你,Redlock 红锁是为了处治主从架构中当出现主从切换导致多个客户端捏有吞并个锁而提议的一种算法。
群众不错看官方文档(https://redis.io/topics/distlock),以下来自官方文档的翻译。
博彩论坛想用使用 Redlock,官方建议在不同机器上部署 5 个 Redis 主节点,节点王人是完竣孤立,也不使用主从复制,使用多个节点是为容错。
一个客户端要获取锁有 5 个要津:
客户端获取面前期间 T1(毫秒级别);
使用交流的 key和 value法例尝试从 N个 Redis实例上获取锁。
每个请求王人修复一个超经常间(毫秒级别),该超经常间要远小于锁的有用期间,这样便于快速尝试与下一个实例发送请求。
比如锁的自动开释期间 10s,则请求的超经常间不错修复 5~50 毫秒内,这样不错防备客户端永劫期阻扰。
客户端获取面前期间 T2 并减去要津 1 的 T1 来狡计出获取锁所用的期间(T3 = T2 -T1)。当且仅当客户端在大无数实例(N/2 + 1)获取奏凯,且获取锁所用的总期间 T3 小于锁的有用期间,才以为加锁奏凯,不然加锁失败。
如若第 3 步加锁奏凯,则履行业务逻辑操作分享资源,key 的的确有用期间等于有用期间减去获取锁所使用的期间(要津 3 狡计的终结)。
如若因为某些原因,获取锁失败(莫得在至少 N/2+1 个 Redis 实例取到锁或者取锁期间照旧杰出了有用期间),客户端应该在总共的 Redis 实例上进行解锁(即便某些 Redis 实例根底就莫得加锁奏凯)。
另外部署实例的数目条目是奇数,为了能很好的餍足过半原则,如若是 6 台则需要 4 台获取锁奏凯才气以为奏凯,是以奇数更合理
事情可没这样浅易,Redis 作家把这个决议提议后,受到了业界闻明的漫步式系统众人的质疑。
两东谈主好比忠良打架,两东谈主一来一趟论据实足的对一个问题提议许多论断……
Martin Kleppmann 提议质疑的博客:https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html
Redlock 联想者的申报:http://antirez.com/news/101
Redlock 是与非Martin Kleppmann 以为锁定的操办是为了保护对分享资源的读写,而漫步式锁应该「高效」和「正确」。
高效性:漫步式锁应该要餍足高效的性能,Redlock 算法向 5 个节点履行获取锁的逻辑性能不高,老本加多,复杂度也高;
正确性:漫步式锁应该防备并发程度在吞并本领只可有一个线程能对分享数据读写。
出于这两点,咱们没必要承担 Redlock 的老本和复杂,运行 5 个 Redis 实例并判断加锁是否餍足大无数才算奏凯。
主从架构崩溃复原极小可能发生,这没什么大不了的。使用单机版就够了,Redlock 太重了,没必要。
Martin 以为 Redlock 根底够不上安全性的条目,也依旧存在锁失效的问题!
Martin 的论断Redlock 不僧不俗:对于偏好成果来讲,Redlock 相比重,没必要这样作念,而对于偏好正确性来说,Redlock 是不够安全的。
时钟假定不对理:该算法对系统时钟作念出了危机的假定(假定多个节点机器时钟王人是一致的),如若不餍足这些假定,锁就会失效。
无法保证正确性:Redlock 不成提供肖似 fencing token 的决议,是以处治不了正确性的问题。为了正确性,请使用有「共鸣系统」的软件,举例 Zookeeper。
Redis 作家 Antirez 的反驳在 Redis 作家的反驳著作中,有 3 个要点:
时钟问题:Redlock 并不需要完竣一致的时钟,只需要大体一致就不错了,允许有「差错」,只消差错不要杰出锁的租期即可,这种对于时钟的精度条目并不是很高,而且这也得当现实环境。
收集蔓延、程度暂停问题:
客户端在拿到锁之前,不管阅历什么耗时长问题,Redlock 王人概略在第 3 步检测出来
客户端在拿到锁之后,发生 NPC,那 Redlock、Zookeeper 王人窝囊为力
质疑 fencing token 机制。
对于 Redlock 的争论咱们下期邂逅,面前插足 Redisson 竣事漫步式锁实战部分。
Redisson 漫步式锁基于 SpringBoot starter 模式,添加 starter。
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.16.4</version> </dependency>
不外这里需要细心 springboot 与 redisson 的版块,因为官方推选 redisson 版块与 springboot 版块互助使用。
将 Redisson 与 Spring Boot 库集成,还取决于 Spring Data Redis 模块。
「码哥」使用 SpringBoot 2.5.x 版块, 是以需要添加 redisson-spring-data-25。
<dependency> <groupId>org.redisson</groupId> <!-- for Spring Data Redis v.2.5.x --> <artifactId>redisson-spring-data-25</artifactId> <version>3.16.4</version> </dependency>
添加成立文献
spring: redis: database: host: port: password: ssl: timeout: # 说明施行情况成立 cluster 或者哨兵 cluster: nodes: sentinel: master: nodes:
就这样在 Spring 容器中咱们领有以下几个 Bean 不错使用:
RedissonClient RedissonRxClient RedissonReactiveClient RedisTemplate ReactiveRedisTemplate 失败无穷重试RLock lock = redisson.getLock("码哥字节"); try { // 1.最常用的第一种写法 lock.lock(); // 履行业务逻辑 ..... } finally { lock.unlock(); }
拿锁失败时会不竭的重试,具有 Watch Dog 自动脱期机制,默许续 30s 每隔 30/3=10 秒续到 30s。
失败超时重试,自动续命// 尝试拿锁10s后罢手重试,获取失败复返false,具有Watch Dog 自动脱期机制, 默许续30s boolean flag = lock.tryLock(10, TimeUnit.SECONDS);超时自动开释锁
// 莫得Watch Dog ,10s后自动开释,不需要调用 unlock 开释锁。 lock.lock(10, TimeUnit.SECONDS);超时重试,自动解锁
// 尝试加锁,最多恭候100秒,上锁以后10秒自动解锁,莫得 Watch dog boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { try { ... } finally { lock.unlock(); } }Watch Dog 自动延时
如若获取漫步式锁的节点宕机,且这个锁还处于锁定景色,就会出现死锁。
为了幸免这个情况,咱们王人会给锁修复一个超时自动开释期间。
关联词,照旧会存在一个问题。
网站提供安全、稳定的博彩平台和专业的博彩攻略和技巧分享,同时还有多样化的博彩游戏和赛事直播,让广大博彩爱好者能够在博彩游戏中体验不同的博彩乐趣。假定线程获取锁奏凯,并修复了 30 s 超时,然而在 30s 内任务还没履行完,锁超时开释了,就会导致其他线程获取不该获取的锁。
是以,Redisson 提供了 watch dog 自动延时机制,提供了一个监控锁的看门狗,它的作用是在 Redisson 实例被关闭前,陆续的延长锁的有用期。
也即是说,如若一个拿到锁的线程一直莫得完成逻辑,那么看门狗会匡助线程陆续的延长锁超经常间,锁不会因为超时而被开释。
默许情况下,看门狗的续期期间是 30s,也不错通过修改 Config.lockWatchdogTimeout 来另行指定。
另外 Redisson 还提供了不错指定 leaseTime 参数的加锁法子来指定加锁的期间。
杰出这个期间后锁便自动解开了,不会延长锁的有用期。
旨趣如下图:
有两个点需要细心:
watchDog 只消在未透露指定加锁超经常间(leaseTime)时才会见效。 lockWatchdogTimeout 设定的期间不要太小 ,比如修复的是 100 毫秒,由于收集径直导致加锁完后,watchdog 去脱期时,这个 key 在 redis 中照旧被删除了。 源码导读在调用 lock 法子时,会最终调用到 tryAcquireAsync。
调用链为:lock()->tryAcquire->tryAcquireAsync,详备讲明注解如下:
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) { RFuture<Long> ttlRemainingFuture; //如若指定了加锁期间,会径直去加锁 if (leaseTime != -1) { ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG); } else { //莫得指定加锁期间 会先进行加锁,况兼默许期间即是 LockWatchdogTimeout的期间 //这个是异步操作 复返RFuture 肖似netty中的future ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG); } //这里亦然肖似netty Future 的addListener,在future内容履行完成后履行 ttlRemainingFuture.onComplete((ttlRemaining, e) -> { if (e != null) { return; } // lock acquired if (ttlRemaining == null) { // leaseTime不为-1时,不会自动脱期 if (leaseTime != -1) { internalLockLeaseTime = unit.toMillis(leaseTime); } else { //这里是定时履行 面前锁自动脱期的动作,leaseTime为-1时,才会自动脱期 scheduleExpirationRenewal(threadId); } } }); return ttlRemainingFuture; }
scheduleExpirationRenewal 中会调用 renewExpiration 启用了一个 timeout 定时,去履行脱期动作。
private void renewExpiration() { ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName()); if (ee == null) { return; } Timeout task = commandExecutor.getConnectionManager() .newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { // 概略部分代码 .... RFuture<Boolean> future = renewExpirationAsync(threadId); future.onComplete((res, e) -> { .... if (res) { //如若 莫得报错,就再次定时脱期 // reschedule itself renewExpiration(); } else { cancelExpirationRenewal(null); } }); } // 这里咱们不错看到定时任务 是 lockWatchdogTimeout 的1/3期间去履行 renewExpirationAsync }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); ee.setTimeout(task); }
scheduleExpirationRenewal 会调用到 renewExpirationAsync,履行底下这段 lua 剧本。
他主要判断即是 这个锁是否在 redis 中存在,如若存在就进行 pexpire 脱期。
protected RFuture<Boolean> renewExpirationAsync(long threadId) { return evalWriteAsync(getRawName(), 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(getRawName()), internalLockLeaseTime, getLockName(threadId)); }watch dog 在面前节点还存活且任务未完成则每 10 s 给锁续期 30s。 标准开释锁操作时因为特殊莫得被履行,那么锁无法被开释,是以开释锁操作一定要放到 finally {} 中; 要使 watchLog 机制见效 ,lock 时 不要修复 逾期期间。 watchlog 的延经常间 不错由 lockWatchdogTimeout 指定默许延经常间,然而不要修复太小。 watchdog 会每 lockWatchdogTimeout/3 期间,去延时。 通过 lua 剧本竣事蔓延。 操心
完工,我建议你合上屏幕,我方在脑子里从新过一遍,每一步王人在作念什么,为什么要作念,处治什么问题。
咱们一齐重新到尾梳理了一遍 Redis 漫步式锁中的各式门谈,其实许多点是不管用什么作念漫步式锁王人会存在的问题,蹙迫的是念念考的过程。
对于系统的联想,每个东谈主的起点王人不相通,莫得完好的架构,莫得普适的架构,然而在完好和普适能均衡的很好的架构,即是好的架构。
本文转载自微信公众号「码哥字节」,不错通过以下二维码爱护。转载本文请关连码哥字节公众号。