Redisson分布式锁(WatchDog分析,浅浅看下源码)

2024-03-19 00:36

本文主要是介绍Redisson分布式锁(WatchDog分析,浅浅看下源码),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

带大家简单了解下Redisson的看门狗机制,这个面试中也比较常见。

目录

  • WatchDog(看门狗)机制
  • 开启WatchDog(看门狗)
  • 浅看下源码

WatchDog(看门狗)机制

Redisson看门狗机制是用于解决在业务运行时间大于锁失效时间的情况,即自动续期,当某用户执行抢占锁执行需要40秒,而锁有效期为30秒,到期后锁就有可能被其他用户抢占,这个时候看门狗机制就可以帮其自动续期至执行结束。

开启WatchDog(看门狗)

引入maven

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

先来看下redisson的简单使用(开门狗机制未开启时

@RequestMapping("/watch_dog")
public String watchDog(){// 简单配置RedissonClientConfig config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);// 获取锁RLock lock = redisson.getLock("anyLock");try {// 尝试获取锁,最多等待3秒,锁定之后3秒自动解锁(锁释放程序照跑)boolean isLocked = lock.tryLock(3, 3, TimeUnit.SECONDS);if (isLocked) {System.out.println(Thread.currentThread().getName()+":还没睡觉");Thread.sleep(3000);System.out.println(Thread.currentThread().getName()+":睡眠了3秒钟");// 业务逻辑Thread.sleep(5000);System.out.println(Thread.currentThread().getName()+":睡眠了5秒钟");}} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁(查询当前线程是否保持锁定)if (lock.isHeldByCurrentThread()) {lock.unlock();}}// 关闭RedissonClientredisson.shutdown();return Thread.currentThread().getName();
}

通过以下结果可以发现,当http-nio-19428-exec-1线程还在执行时,3秒后锁过期导致http-nio-19428-exec-4就可以抢占了锁

在这里插入图片描述

开启看门狗机制,以下是各类情况:
//
// //拿锁失败会不停重试
// //具有 Watch Dog 自动延期机制,默认续30s 每隔30/3=10 秒续到30s
// lock.lock();
//
// //businessLock.tryTime() 秒之后停止重试加锁,返回false
// //具有 Watch Dog 自动延期机制,默认续30s 每隔30/3=10 秒续到30s
// boolean locked1 = lock.tryLock(businessLock.tryTime(), businessLock.timeUnit());
//
// //businessLock.tryTime() 秒之后停止重试加锁,返回false
// //不具有 Watch Dog 自动延期机制
// boolean locked2 = lock.tryLock(businessLock.tryTime(), businessLock.expire(), businessLock.timeUnit());
//
// //businessLock.tryTime() 秒之后停止重试加锁,返回false
// //只有 leaseTime(默认-1) 等于 -1 时(示具体版本情况而定),才具有 Watch Dog 自动延期机制,默认续30s 每隔30/3=10 秒续到30s
// boolean locked3 = lock.tryLock(businessLock.tryTime(), -1, businessLock.timeUnit());

lock()方法是阻塞获取锁的方式,如果当前锁被其他线程持有,则当前线程会一直阻塞等待获取锁,直到获取到锁或者发生超时或中断等情况才会结束等待。该方法获取到锁之后可以保证线程对共享资源的访问是互斥的,适用于需要确保共享资源只能被一个线程访问的场景。Redisson 的 lock() 方法支持可重入锁和公平锁等特性,可以更好地满足多线程并发访问的需求。
tryLock()方法是一种非阻塞获取锁的方式,在尝试获取锁时不会阻塞当前线程,而是立即返回获取锁的结果,如果获取成功则返回 true,否则返回false。Redisson 的 tryLock() 方法支持加锁时间限制、等待时间限制以及可重入等特性,可以更好地控制获取锁的过程和等待时间,避免程序出现长时间无法响应等问题。
按个人理解:lock()会一直自旋等待锁,而tryLock()尝试获取锁后快速返回结果

以最后一种情况为例,修改代码如下:

//尝试获取锁,最多等待6秒
boolean isLocked = lock.tryLock(6, -1, TimeUnit.SECONDS);

通过以下结果可以看出,在http-nio-19428-exec-2无法并没有自动释放锁
在这里插入图片描述

这时候大家就会问了,不是没设置过期时间么,当然不会自动失效啦,我也是带着这个疑问,饭约了资料以及一些博主的讲解,其实redisson的看门狗机制主要是用于以下情况的:
分布式锁在执行过程中若锁失效的情况则会导致锁被其他线程占用,但是若锁不设置失效时长,虽然有逻辑控制释放锁,若出现宕机时则会导致锁未释放而死锁,而redission的看门狗机制就可以解决这个死锁问题,宕机后在默认的配置下最长30s 的时间后,这个锁就自动释放了。

浅看下源码

//  源码这里解释前面三四点为何出现leaseTime问题,该版本为leaseTime>0即不触发
//  源码跟进 tryLock->tryAcquire->tryAcquireAsync

在这里插入图片描述

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {return this.evalWriteAsync(this.getRawName(), 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.getRawName()), new Object[]{unit.toMillis(leaseTime), this.getLockName(threadId)});
}

执行 Redis 的 Lua 脚本来加锁

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]);

具体的源码解析我觉得可以看看该博客 https://www.cnblogs.com/Leo_wl/p/16600565.html

这篇关于Redisson分布式锁(WatchDog分析,浅浅看下源码)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/824220

相关文章

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

Java 线程池+分布式实现代码

《Java线程池+分布式实现代码》在Java开发中,池通过预先创建并管理一定数量的资源,避免频繁创建和销毁资源带来的性能开销,从而提高系统效率,:本文主要介绍Java线程池+分布式实现代码,需要... 目录1. 线程池1.1 自定义线程池实现1.1.1 线程池核心1.1.2 代码示例1.2 总结流程2. J

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja