您的位置:首页 > 游戏 > 游戏 > 企业营销策划有限公司_成都尚舍设计公司_东莞软文推广_百度免费官网入口

企业营销策划有限公司_成都尚舍设计公司_东莞软文推广_百度免费官网入口

2025/2/12 12:28:27 来源:https://blog.csdn.net/cl939974883/article/details/144808204  浏览:    关键词:企业营销策划有限公司_成都尚舍设计公司_东莞软文推广_百度免费官网入口
企业营销策划有限公司_成都尚舍设计公司_东莞软文推广_百度免费官网入口

文章目录

  • 前言
  • 前言问题解决思路
  • 如何开启redission看门狗
  • 看门狗核心代码
    • 定时续期实现
    • 解锁实现
  • Redission的bug相关记录
  • Redission依旧会产生需要考虑的问题

封面

前言

本篇博客是介绍redission看门狗实现redis定期续期原理,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

通常我们在使用redis上锁时,会进行业务处理,通常上锁我们会采用setnx或者lua脚本来保证并发场景下,只有一个线程能够抢到锁。

大多数的业务代码为:

setnx lockKey,超时时间
业务代码
delete lockKey

一旦超时时间过去了,redis对应的key就过期掉了。

场景说明:如果说key过期了,但是其中的业务在超时时间里没有完成,那么就可能会出现并发执行我们业务的问题,导致出现各种并发问题。


前言问题解决思路

案例demo来源:https://developer.aliyun.com/article/1627823

锁过期问题解决:

  1. 预估业务执行时间,提前预判业务执行时长。(一旦预估不准,就会导致并发问题)
  2. 模拟CAS乐观锁的方式,增加版本号。(侵入代码)
  3. watch dog自动延期机制。(起一个后台线程,默认超时时长为30s,每隔10s检查一次,一旦客户端还持有锁,就会不断延长key的过期时间)

解决思路2:在每次写操作时加入一个 token。 token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次。

在每次写操作时加入一个 token。 token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次。

示例如下:

首先客户端1获取到了token值43,一旦客户端1出现STW或者业务时间较长情况,出现了redis key过期的情况,锁被释放。
此时客户端2获取到了token值44,执行的很快,业务完成后又将token=44写入db。
此时客户端1这时候活过来了,执行完业务再进行插入的时候,由于当前其token=43是<44,表示该业务出现了锁过期并发情况,那么该写入操作就会被拒绝。

image-20241228122131094

缺点:入侵代码。

解决思路3:watch dog自动延期

客户端1进行加锁的同时,开启一个后台线程,这个线程每10s会检查下客户端1是否还持有锁,一旦还持有就会给它进行续期10s。

**本质:**在锁没有过期之前,不断的延长锁的有效期。

**优点:**不入侵业务代码,能够在真正业务执行完成之后锁释放。

**缺点:**无法避免STW情况,一旦出现STW无法执行续期代码逻辑。


如何开启redission看门狗

// 参数传递-1,开启看门狗,默认leaseTime超时时长为30s
boolean lock = rLock.tryLock(lockWaitTime,-1,unit);// 选择方法参数重不带leaseTime的,默认设置参数为-1 开启看门狗,默认leaseTime超时时长为30s
boolean lock = rLock.tryLock(lockWaitTime,unit);

一旦开启看门狗之后,会每隔leaseTime/3的时间进行续期leaseTime/3。


看门狗核心代码

下面只是指出最核心代码,并无细致逐行代码分析。

定时续期实现

业务代码:

RLock rLock = redissonClient.getLock(lockKey);
try {boolean lock = rLock.tryLock(lockWaitTime,lockLeaseTime,unit);

image-20241228202542859

tryLock上锁

image-20241228125413910

image-20241228125430879

该方法为定时续期:

image-20241228125520325

image-20241228125558878

image-20241228202702008

该段代码进行续期:主要判断就是 这个锁是否在redis中存在,如果存在就进行 pexpire 延期

image-20241228202802159

protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {return this.evalWriteAsync(this.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(this.getRawName()), this.internalLockLeaseTime, this.getLockName(threadId));
}

解锁实现

image-20241228203038402

image-20241228203143321

Redission的bug相关记录

当前项目中使用的redission版本为:

  <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.17.7</version></dependency>

Redission 版本3.16.0:

  • 看门狗不生效问题 mr:https://github.com/redisson/redisson/issues/371。

Redisson 版本3.12.0:

  • 看门狗不生效问题 mr:https://github.com/redisson/redisson/issues/2515

Redission 版本3.10.3:

  • 看门狗死锁问题 mr:https://github.com/redisson/redisson/issues/1966

Redission依旧会产生需要考虑的问题

相关解决方案思路:

  • 如何解决Redisson中的WatchDog因JVM GC SWT存在的失效风险?:https://segmentfault.com/q/1010000044673539
  • redis分布式锁的思考:https://blog.csdn.net/weixin_43887958/article/details/118336001

下面列举可能遇到的场景:

  • JVM STW导致的锁过期问题,也就是JVM整个层面的stw情况,stw导致所有的线程都无法进行正常工作,如果时间过长,导致锁过期了,那么就需要在释放锁的时候去判断当前锁是否存在,不存在的话就抛出异常,若存在且是别的线程拿到的锁,也要抛出异常。
  • 另一种极端的情况:若是redis主从集群,那么有可能在异步复制时,主节点返回成功给线程1后,并未将数据同步到从节点就宕机了,之后从节点被升级为主节点,线程2也去获取锁A成功,线程1也获取成功。

**说明:**需要做一些补偿措施,一般的补偿措施就是将事务回滚。

**实现思路:**线程释放锁的时候应该判断当前锁是否存在,若不存在(1.key A过期,2.主节点未将key A同步到从节点就宕机了)就一定要回滚事务。在上述线程1和2都拿到锁的情况下,有没有问题呢?其实是没什么大问题的,因为线程A释放锁的时候会发现持有者不是自己,释放不掉,这个时候怎么办?只需要做一下补偿就行了,例如将增删改的数据还原即可。


版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com