Redisson解决Redis分布式锁提前释放问题
简介:在分布式场景中,相信大家或多或少都需要使用分布式锁来访问重要资源或者控制耗时操作的并发度。
当然,实现分布式锁的方案有很多,比如数据库、Redis、ZooKeeper等。
本文主要收集了一个网上的案例来讲解redis分布式锁的相关实现。
某天线处理数据冗余时出现问题,经排查发现单次处理时间较长,分布式redis锁提前释放,导致。
并发处理。
同样的要求。
实际上,这是一个锁更新问题。
对于分布式锁,我们需要考虑设置的锁需要多长时间才会过期,以及如果发生异常如何释放锁?
以上问题就是本文要讨论的主题。
项目使用? 一个比较简单的自定义分布式锁,设置默认过期时间10秒,避免死锁,如下:
overridefunlock(){while (true){//尝试获取锁 if(tryLock()){返回}尝试{Th read.sleep(10)}catch(e:InterruptedException){e.printStackTrace()}}}overridefuntryLock():Boolean{valvalue=getUniqueSign()//随机字符串 v aflag=redisTemplate!!.opsForValue().setIfAbsent(name,value,10000,TimeUnit.MILLISECONDS)if(flag!=null&&flag){VALUE_lOCAL.set(v) alue)INTO_NUM_LOCAL.set(if(INTO_NUM_LOCAL.get()!=null)INTO_NUM_LOCAL.get()+1else1)returntrue}returnfalse缺乏自动锁更新实现。
在这种场景下,你可以思考如何自动更新锁——当工作没有完成时,当然你也可以自定义实现,比如解锁后台线程定期更新锁这些线程的锁。
Redisson也是依靠这个思想来实现自动更新的分布式锁,各种异常情况也被充分考虑,综合考虑了Redisson分布式锁解决方案的改进。
setRetryAttempts(singleServerConfig)。
.retryAttempts).setTimeout(singleServerConfig.timeout)returnRedisson.create(config)}}@ConfigurationProperties(pre fix="xxx.redisson")classRedissonProperties{varsingleServerConfig:SingleServerConfig?=null}
Redis服务采用腾讯云哨兵模式架构。
该结构体开放了一个代理地址供外部访问,因此您可以在此处配置独立模式配置。
如果自己创建redis Sentinel模式结构体,则需要根据文档配置相关必要参数
3、使用示例: @AutowiredlateinitvarredissonClient:RedissonClient funxxx () {.. .vallock=redissonClient.getLock("mylock")lock.lock()try{ }finally{lock.unlock()}使用和错误 JDK提供的锁很相似? 是不是很简单呢?
正是Redisson这样优秀的开源产品的出现,让我们能够投入更多的时间在业务开发上……
4、源码分析我们来看看Redisson的传统分布式锁的实现主要分解RedissonLock
1 erridepublicvoidlock(){try{lock(-1,null,false);}catch(InterruptedExceptione){thrownewIllegalStateException();}}//租期即过期时间,-1表示如果不设置则系统默认为30sprivatevoidlock(longleaseTime,TimeUnitunit,booleaninterruptible) throwsInterruptedException{//尝试获取 锁,如果能拿到就直接回去 longthreadId=Thread.currentThread().getId();Longttl=tryAcquire(-1,leaseTime,unit,threadId);//lockacquiredif(ttl==null){return;}RFuture这次锁失败,直接返回这个key对应的过期时间 returnevalWriteAsync(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]); "+"returnnil;"+"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]);"+"returnnil;"+"end;"+"returnredis.call('pttl',KEYS [1]);, 公司 llections.singletonList(getRawName()),unit.toMillis(leaseTime),getLockName(threadId)); 1.2.续订
由 sch eduleExpirationRenewal 续订 锁
protectedvoidscheduleExpirationRenewal(longthreadId){ExpirationEntryentry=newExpirationEntry();ExpirationEntryoldEntry=EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(),entry);if(oldEnt) ( ee==null){return;}//设置设置重要任务 Late,持续时间InternalLockLeaseTime/3后执行,定期更新锁 Timeouttask=commandExecutor.getConnectionManager().newTimeout(newTimerTask(){@Overridepublicvoidrun(Timeouttimeout)throwsException{ExpirationEntryent=EXPIRATION_RENEWAL_ MAP.get(getEntryName());if(ent==null){return;}LongthreadId=ent.getFirstThreadId();if(threadId==null){return ;}//更新命令操作已经执行 RFuture<布尔> future=renewExpirationAsync(threadId);future.onComplete((res,e)->{if(e!=null){ log.error("Can'tupdatelock"+getRawName()+"expiration",e);EXPIRATION_RENEWAL_MAP.remove(getEntryName());return;}//本次续订后,继续给自己安排,以达到持续续订的效果if( res ){//rescheduleitselfrenewExpiration();}});}},internalLockLeaseTime/ 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);}//所谓续订,就是延长过期时间 protectedRFuture
2. 解锁过程 publicvoidunlock(){try{get(unlockAsync(Thread.currentThread().getId()));}catch(RedisExceptione){. .}}publicRFuture 以上就是redisson客户端工具添加/解锁分布式redis锁的具体实现,主要解决以下问题 ? ?1. 死锁问题:设置过期时间 2、重入问题:重入+1,释放锁-1,当value = 0时,表示完全释放锁 ? ?3. 更新问题:可以解决锁提前释放的问题 4版本:谁持有锁谁就释放 本文是通过网上提问提交的,并且通过流行的实现方案针对redis分布式锁,最终选择了redisson方案; Redisson具体实现细节分析 相关参考: Redisson官方文档-分布式锁和同步器 Redisson官方文档-配置方法 CSDN-如何使用Redis实现分布式锁? Redisson概述 Redisson是一个基于Redis的Java数据网格。
Redisson、Jedis 和 Lettuce 之间的区别在于,它提供了更高级别的抽象,并且更易于使用。
分布式锁 分布式锁是并发业务中的关键组件,用于保证多个进程或线程对共享资源的互斥访问。
Redisson提供了一种使用Lua脚本实现原子操作的简单方法,保证锁的获取和释放过程是原子的,从而避免并发问题。
为了确保重入,Redisson 在 Lua 脚本中引入了一个计数器,用于跟踪同一线程获取同一锁的次数。
此外,Redisson还支持分布式锁扩展功能,允许您在锁时间到期前自动延长锁时长,保证业务运行不间断。
RLock RLock是Redisson实现的核心分布式锁接口。
加锁过程通常涉及以下步骤: 当尝试获取锁。
解锁过程包括从Redis中删除与锁对应的哈希表或键,并更新计数器以确保锁被正确释放。
公平锁 Redisson也提供了公平锁的实现。
总结 Redisson作为一个强大的分布式工具,通过提供丰富的分布式对象和高级服务,简化了分布式编程。
最强分布式锁工具:Redisson
它提供了许多通用的分布式Java对象和分布式服务,旨在简化分布式操作,提高开发效率。
Redisson不仅提供基本的分布式对象,还提供高级的分布式服务,可以解决许多常见的分布式问题。
Jedis和Lettuce主要提供Redis命令的封装,而Redisson则基于Redis、Lua和Netty打造成熟的分布式解决方案。
官方甚至推荐使用Redisson作为Redis工具包。
分布式锁实现通常包括顺序节点、表级锁、悲观锁、乐观锁和 Redis setNx 命令。
Lua 脚本使用 Redis eval 命令执行,该命令提供一次性原子操作并强制锁定互斥性。
解锁后,计数器递减直至达到零,并执行删除操作以确保锁被正确释放。
这个特性在高层场景下尤其重要并发性,可以有效防止锁丢失或超时引起的并发冲突。
它继承自Java并行包中的Lock接口和Redisson RLockAsync用户接口。
RLock提供了锁定和解锁方法来实现Netty上异步操作的基本通信。
锁,Redisson会在Redis操作中使用Lua脚本来实现加锁逻辑和锁计数。
如果锁超时时间不为-1,则会运行定时任务(watchDog)自动续订锁,以保证业务执行完成之前锁不会过期。
它使用Redis List和ZSet数据结构来实现公平队列,并按照线程请求的顺序分配锁,保证公平性。
线程获取锁。
公平锁通过Lua脚本维护Redis中的等待队列和超时设置,并实现获取锁、释放锁和计数锁的逻辑。
提供分布式锁、多机锁、红锁等场景的解决方案,适合需要分布式锁功能的项目。
借助Redisson,开发人员可以更加专注于业务逻辑的实现,而将分布式问题的解决交给Redisson。