当前位置:首页 >> 核电技术聚变聚能设备 >> 【SpringBoot】Redission 的使用与介绍,漫步者r1600t08

【SpringBoot】Redission 的使用与介绍,漫步者r1600t08

cpugpu芯片开发光刻机 核电技术聚变聚能设备 2
文件名:【SpringBoot】Redission 的使用与介绍,漫步者r1600t08 【SpringBoot】Redission 的使用与介绍 背景:

我想我们用到 Redisson 最多的场景一定是分布式锁,一个基础的分布式锁具有三个特性:

互斥:在分布式高并发的条件下,需要保证,同一时刻只有有一个线程获得锁,这是最基本的一点。

防止死锁:在分布式高并发的条件下,比如有个线程获得锁的同时,还没有来的及去释放锁,就因为系统故障或者其它原因使它无法执行释放锁的命令,导致其它线程都无法获得锁,造成死锁。

可重入:我们知道 ReentrantLock 是可重入锁,那它的特点就是同一个线程可以重复拿到同一个资源的锁。

一、SpringBoot 应用 1、通过 Maven 引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.14.0</version></dependency> 2、在 yml 文件中添加配置 spring:redis:host: 127.0.0.1port: 6379timeout: 10000password:lettuce:pool:#最大连接数据库连接数,设 0 为没有限制max-active: 8#最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。max-wait: 1000#最大等待连接中的数量,设 0 为没有限制max-idle: 500#最小等待连接中的数量,设 0 为没有限制min-idle: 300jedis:pool:max-active: 8max-wait: 1000max-idle: 500min-idle: 300 3、自定义配置类 @Configurationpublic class RedissonConfig {@Value("${spring.redis.password}")private String password;@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;/*** 对 Redisson 的使用都是通过 RedissonClient 对象* @return*/ @Bean(name = "redissonClient", destroyMethod = "shutdown") // 服务停止后调用 shutdown 方法public RedissonClient redissonClient() {// 1、创建配置Config config = new Config();// 2、集群模式// config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");// 根据 Config 创建出 RedissonClient 示例config.useSingleServer().setPassword(StringUtils.isEmpty(password) ? null : password).setAddress(host.contains("://") ? "" : "redis://" + host + ":" + port);return Redisson.create(config);}} 4、测试配置类 @Autowiredprivate RedissonClient redissonClient;@Testpublic void test() {System.out.println(redissonClient); // org.redisson.Redisson@40a8a26f} 二、使用 Redisson 分布式锁

用锁的一般步骤:

获取锁实例(只是获得一把锁的引用,并不是占有锁)通过锁实例加锁(占有了这把锁)通过锁实例释放锁

Redisson 提供很多种类型的锁,其中最常用的就是可重入锁(Reentrant Lock)了。

Redisson 中的可重入锁 1、获取锁实例 RLock lock = redissonClient.getLock(String lockName);

获取的锁实例实现了 RLock 接口,而该接口拓展了 JUC 包中的 Lock 接口,以及异步锁接口 RLockAsync。

2、通过锁实例加锁(Rlock 常用的方法)

从同步与异步特性来区分,加锁方法可分为同步加锁和异步加锁两类。异步加锁方法的名称一般是在相应的同步加锁方法后加上“Async”后缀。 从阻塞与非阻塞特性来区分,加锁方法可分为阻塞加锁和非阻塞加锁两类。非阻塞加锁方法的名称一般是“try”开头。

下面以比较常用的同步加锁方法来说明加锁的一些细节。

阻塞加锁的方法: void lock():lock 表示如果当前锁可用,则加锁成功,并立即返回,没有返回值,继续执行下面代码;如果当前锁不可用,则阻塞等待直至锁可用(当前锁失效时间默认30 s),然后返回。 //创建锁RLock helloLock = redissonClient.getLock("hello");//加锁helloLock.lock();try {log.info("locked");Thread.sleep(1000 * 10);} finally {//释放锁helloLock.unlock();}log.info("finished"); void lock(long leaseTime, TimeUnit unit):加锁机制与 void lock() 相同,只是增加了锁的有效(租赁)时长 leaseTime。加锁成功后,可以在程序中显式调用 unlock() 方法进行释放;如果未显式释放,则经过 leaseTime 时间,该锁会自动释放。如果 leaseTime 传入 -1,则会开启看门狗机制,跟上面 lock() 方法意义一致。 非阻塞加锁的方法: boolean tryLock():(JUC 中 Lock 接口定义的方法)调用该方法会立刻返回。返回值为true则表示锁可用,加锁成功;返回值为false则表示锁不可用,加锁失败。boolean tryLock(long time, TimeUnit unit):尝试去加锁(第一个参数表示 the maximum time to wait for the lock),如果锁可用则立刻返回 true,继续执行 true 下面代码,否则最多等待 time 长的时间(如果 time<=0,则不会等待)。在 time 时间内锁可用则立刻返回 true,time 时间之后返回 false。如果在等待期间线程被其他线程中断,则会抛出  InterruptedException 异常。 String key ="product:001";RLock lock = redisson.getLock(key);try {boolean res = lock.tryLock(10,TimeUnit.SECONDS);if ( res){System.out.println("这里是你的业务代码");} else {System.out.println("系统繁忙");}} catch (Exception e){e.printStackTrace();} finally {lock.unlock();} boolean tryLock(long waitTime, long leaseTime, TimeUnit unit):与 boolean tryLock(long time, TimeUnit unit) 类似,只是增加了锁的使用(租赁)时长 leaseTime。表示尝试去加锁(第一个参数表示等待时间,第二个参数表示 key 的失效时间),加锁成功,返回true,继续执行 true 下面代码。如果返回 false,它会等待第一个参数设置的时间,然后去执行 false 下面的代码。 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);

解读:尝试加锁,最多等待 100 秒,上锁以后 10 s自动解锁,没有 watch dog 机制。

3、通过锁实例释放锁

void unlock():释放锁。如果当前线程是锁的持有者(即在该锁实例上加锁成功的线程),则会释放成功,否则会抛出异常。

4、异步执行分布式锁 /*** 异步锁*/lock = redissonClient.getLock("erbadagang-lock");Future<Boolean> res = null;try {// lock.lockAsync();// lock.lockAsync(100, TimeUnit.SECONDS);res = lock.tryLockAsync(3, 100, TimeUnit.SECONDS);if (res.get()) {System.out.println("这里是你的Async业务代码");} else {System.out.println("系统繁忙Async");}} catch (Exception e) {e.printStackTrace();} finally {if (res.get()) {lock.unlock();}}log.info("finished");

三、一般编程范式 1、同步阻塞加锁 String lockName = ...RLock lock = redissonClient.getLock(lockName);// 阻塞式加锁lock.lock();try {// 操作受锁保护的资源} finally {// 释放锁lock.unlock();} 2、同步非阻塞加锁 String lockName = ...RLock lock = redissonClient.getLock(lockName);if (lock.tryLock()) {try {// 操作受锁保护的资源} finally {lock.unlock();}} else {// 执行其他业务操作} 四、分布式锁分析

优秀的分布式锁需要具备以下特性:

互斥性:在任意时刻,只有一个客户端(线程)能持有锁,这是锁的基本要求。锁的可重入:同一个客户端能多次持有同一把锁。实现上只要检查锁的持有者是否为当前客户端,若是则重入锁成功,并将锁的持有数加1。一般通过给每个客户端分配一个唯一的ID,并在加锁成功时向锁中写入该ID即可。不会因客户端异常而长久锁住:当客户端在持有锁期间崩溃而未主动解锁时,锁也会在一定时间后自动释放,即锁有超时自动释放的特性。解锁的安全性:加锁和解锁必须是同一个客户端,客户端不能把别人加的锁给释放了,即不能误解锁。实现上与锁的可重入类似,在释放锁时检查客户端ID与锁中保存的ID是否一致即可。

Redisson的分布式锁除了实现上述几个特性外,还具有锁的自动续期功能。即当我们加锁而未指定锁的有效时长时,Redisson会按一定的周期,定时检查当前线程是否活跃,若是则自动为锁续期,这一特性称为watchdog(看门狗)机制。 有了这个特性,我们就可以不必为设定锁的有效时间而纠结了(设得太长,则会在客户端崩溃后仍长时间占有锁;设得太短,则可能在业务逻辑执行完成前,锁自动释放),Redisson分布式锁可以在客户端崩坏时自动释放,业务逻辑未执行完时自动续期。

五、看门狗机制 // 拿锁失败时,会不停的重试RLock lock = redissonClient.getLock("guodong");// 具有 watch Dog 自动延期机制,默认续 30 slock.lock();// 尝试拿锁10s后停止重试,返回false,具有 watch dog 自动延期机制默认续30sboolean res1 = lock.tryLock(10, TimeUnit.SECONDS); // 没有 watch dog,10s后自动释放lock.lock(10, TimeUnit.SECONDS);// 尝试拿锁100s后停止重试,返回false,没有 watch Dog,10s后自动释放boolean res2 = lock.tryLock(100, 10, TimeUnit.SECONDS);Thread.sleep(40000L);lock.unlock();  5.1 Watch Dog 的自动延期机制

如果拿到分布式锁的节点宕机,且这个锁正好处于锁住的状态时,会出现锁死的状态,为了避免这种情况的发生,锁都会设置一个过期时间。这样也存在一个问题,加入一个线程拿到了锁设置了30s超时,在30s后这个线程还没有执行完毕,锁超时释放了,就会导致问题,Redisson 给出了自己的答案,就是 watch dog 自动延期机制。

Redisson 提供了一个监控锁的看门狗,它的作用是在 Redisson实例被关闭前,不断地延长锁的有效期,也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断地延长锁超时时间,锁不会因为超时而被释放。

默认情况下,看门狗的续期时间是30s,也可以通过修改 Config.lockWatchdogTimeout 来另行指定。另外 Redisson 还提供了可以指定 leaseTime 参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

5.2 结论 watch dog 只有在未显示指定加锁时间(leaseTime)时才会生效。(这点很重要)watch dog 如果指定加锁时间(leaseTime = -1),也是会开启看门狗机制的watch dog 在当前节点存活时每 10 s给分布式锁的 key 续期 30swatch dog 机制启动,且代码中没有释放锁操作时,watch dog 会不断地给锁续期如果程序释放锁操作时因为异常没有被执行,那么锁无法被释放,所以释放锁操作一定要放到 finally {} 中要使 watch dog 机制生效,lock 时不要设置过期时间watch dog 的延时时间可以由 lockWatchDogTimeout 指定默认延时时间,但是不要设置太小watch dog 会每 lockWatchDogTimeout / 3 时间去延时watch dog 通过类似 netty 的 Future 功能来实现异步延时watch dog 最终还是通过 lua 脚本来进行延时 五、参考文档 Redisson分布式锁在Java应用中的使用Spring Boot中使用Redisson

redisson中的看门狗机制总结
协助本站SEO优化一下,谢谢!
关键词不能为空
同类推荐
«    2025年12月    »
1234567
891011121314
15161718192021
22232425262728
293031
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接