当前位置: 首页 > 科技观察

Redisson 分布式锁源码之一:可重入锁加锁_0

时间:2023-03-18 00:59:40 科技观察

Redisson分布式锁源码之一:Reentrantlock和lockLocks,如synchronize、ReentrantLock等,如何处理分布式系统?当然是使用分布式锁。如果你不知道什么是分布式锁,建议看看石山老师的惊喜课或者上网搜索相关资料。在使用Redis作为分布式锁时,目前使用的框架是Redisson。当然,Redisson不仅仅是作为一把锁,还有很多其他的功能。大家可以看看官方文档,自己实践一下。下面开始记录Redisson的相关笔记吧!如果你错了,请纠正我。1.环境配置本地环境搭建伪集群:不同版本的redisson3.15.6可能不一样,但核心思想不会有太大变化。如果有大的变化,请留言。org.redissonredisson3.15.6项目准备一个简单的maven工程,只需要一个Main方法。2.将可重入锁作为源码入口添加到lock.lock()断点。默认锁定,不传递任何参数。但是这里会设置leaseTime=-1。这个leaseTime的意思就是加锁时间。剩下的就继续吧。在调用tryAcquire方法之前,多了一个参数threadId,它是当前线程的id,是一个正长整数。异步加锁直接来tryAcquireAsync异步加锁方法。tryAcquireAsync里面已经说了leaseTime为-1,所以这里会转到下面的方法。至此几个参数已经明确:waitTime:-1;internalLockLeaseTime:使用默认时间30000毫秒;TimeUnit.MILLISECONDS:单位毫秒;threadId:线程id;RedisCommands.EVAL_LONG:评估。Rediseval命令的相关文档可以阅读:https://redis.io/commands/eval加锁逻辑真正的加锁逻辑其实就是这么一个lua脚本。先解释一下lua脚本的参数信息:KEYS[1]:getRawName(),锁定的key,比如anyLock;ARGV[1]:unit.toMillis(leaseTime),加锁的毫秒时间,比如30000;ARGV[2]:getLockName(threadId),是UUID和线程id拼接的字符串,比如931573de-903e-42fd-baa7-428ebb7eda80:1。因为使用了lua脚本,所以可以保证这段lua脚本的原子性。第一次加锁分析:exists命令判断redisanyLock是否存在;如果不存在,使用hincrby命令创建anyLock数据;设置anyLock的过期时间。加锁后,Redis中的数据格式为:关于Redis的Hash数据结构,可以阅读:https://redis.io/topics/data-types#hashes稍微抽象一点可以理解为一个K-V结构的数据挂在下面anyLock:"anyLock":{"f400aad5-4b1f-4246-a81e-80c2717c3afb:1":"1"}后面执行脚本的内容是请求执行lua脚本。唯一需要注意的是哈希槽路由。这段代码在CommandAsyncService#evalWriteAsync方法中被调用以获取NodeSource。当然,这个NodeSource中只存储了一个slot(hashslotvalue)。该槽值是使用锁定密钥的CRC16算法计算得出的。//MAX_SLOT默认为16384intresult=CRC16.crc16(key.getBytes())%MAX_SLOT;计算槽有什么用?继续追踪!BaseRedisBatchExecutor#addBatchCommandData会从这里的source中获取solt,然后获取MasterSlaveEntry。可以理解为这里获取到的Rediskey对应的节点。Reentrant由于是可重入锁,所以这个块是支持可重入的。让我们看看如何保证重入。exists命令判断redis的key字段是否存在;如果存在,使用hincrby命令自增key字段对应的值;设置当前rediskey的过期时间。以上加锁互斥验证了两种情况:rediskey不存在;redis键和键的字段存在。剩下的情况是键存在,但字段不存在。要知道key的字段是UUID:ThreadId,也就是说锁不是当前线程。这个时候直接返回当前锁的剩余时间。3.总结本文主要介绍Redisson的可重入锁、锁重入、锁互斥的逻辑。核心重点是lua脚本。同时,你需要了解Redis的Hash数据结构。同时需要记住,不指定锁定时间时,默认使用30s。最后一张图介绍本文的加锁逻辑。