Redisson源码分析

技术干货,第一时间推送

1 概述

在redisson之前,很多人可能已经自己实现过基于redis的分布式锁,本身原理也比较简单,redis自身就是一个单线程处理器,具备互斥的特性,通过setNx,exist等命令就可以完成简单的分布式锁,处理好超时释放锁的逻辑即可。

1 源码分析

redisson在此基础上,加上了更多的逻辑控制和功能,譬如公平锁等。这一篇我们就来看看redisson是如何完成分布式锁的。

先是使用锁的地方,在上一篇里已经用了。先获取RLock对象,调用lock、tryLock方法来完成加锁的功能。之后执行完毕需要被加锁的逻辑后,释放锁。

lock方法是直接加锁,如果锁已被占用,则直接线程阻塞,进行等待,直到锁被占用方释放。

tryLock方法则是设定了waitTime(等待时间),在这个等待时间没到前,也是线程阻塞并反复去获取锁,直到取到锁或等待时间超时,则返回false。

这里就以tryLock的源码为例来看看。pom里依赖的redisson版本是

可以看到,其中主要的逻辑就是尝试加锁,成功了就返回true,失败了就进入死循环反复去尝试加锁。中途还有一些超时的判断。逻辑还是比较简单的。

再看看tryAcquire方法

这个方法的调用栈也是比较多,之后会进入下面这个方法

该方法就是与redis通信的地方,通过exists key的方法来判断是否已经上锁,如果没锁,则会返回null,锁了则返回超时时间。

回到那个死循环的地方:

这里有一个针对waitTime和redis锁住的key的超时时间大小的比较,取到二者中比较小的那个值,然后用java的Semaphore信号量的tryAcquire方法来阻塞线程。

那么Semaphore信号量又是由谁控制呢,何时才能release呢。这里又需要回到上面来看。

注意这个subscribe方法,它会进入到PublishSubscribe.java中

在这里可以看到,将当前的threadId添加到一个AsyncSemaphore中,并且设置一个redis的监听器,这个监听器是通过redis的发布、订阅功能实现的。如果不了解redis发布订阅的可以去百度查查。一旦监听器收到redis发来的消息,就从中获取与当前thread相关的,如果是锁被释放的消息,就立马通过操作Semaphore来让刚才阻塞的地方释放。

释放后,线程继续执行,仍旧是判断是否已经超时。如果还没超时,就进入下一次循环。

再次去获取锁,取到了就返回true,取不到,继续刚才的步骤。

所以,总体流程应该是这样的。

3 知识补充

可重入加锁机制

那如果客户端1都已经持有了这把锁了,结果可重入的加锁会怎么样呢?

比如下面这种代码:

这时我们来分析一下上面那段lua脚本。

第一个if判断肯定不成立,“exists myLock”会显示锁key已经存在了。

第二个if判断会成立,因为myLock的hash数据结构中包含的那个ID,就是客户端1的那个ID,也就是“8743c9c0-0795-4907-87fd-6c719a6b4586:1”

此时就会执行可重入加锁的逻辑,他会用:

通过这个命令,对客户端1的加锁次数,累加1。

此时myLock数据结构变为下面这样:

大家看到了吧,那个myLock的hash数据结构中的那个客户端ID,就对应着加锁的次数

其实说白了,就是每次都对myLock数据结构中的那个加锁次数减1。

如果发现加锁次数是0了,说明这个客户端已经不再持有锁了,此时就会用:

“del myLock”命令,从redis里删除这个key。

然后呢,另外的客户端2就可以尝试完成加锁了。

这就是所谓的分布式锁的开源Redisson框架的实现机制。

一般我们在生产系统中,可以用Redisson框架提供的这个类库来基于redis进行分布式锁的加锁与释放锁。

释放锁机制

如果执行lock.unlock(),就可以释放分布式锁,此时的业务逻辑也是非常简单的。

其实说白了,就是每次都对myLock数据结构中的那个加锁次数减1。

如果发现加锁次数是0了,说明这个客户端已经不再持有锁了,此时就会用:

“del myLock”命令,从redis里删除这个key。

然后呢,另外的客户端2就可以尝试完成加锁了。

这就是所谓的分布式锁的开源Redisson框架的实现机制。

一般我们在生产系统中,可以用Redisson框架提供的这个类库来基于redis进行分布式锁的加锁与释放锁。

上述Redis分布式锁的缺点

其实上面那种方案最大的问题,就是如果你对某个redis master实例,写入了myLock这种锁key的value,此时会异步复制给对应的master slave实例。

但是这个过程中一旦发生redis master宕机,主备切换,redis slave变为了redis master。

接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。

此时就会导致多个客户端对一个分布式锁完成了加锁。

这时系统在业务语义上一定会出现问题,导致各种脏数据的产生

所以这个就是redis cluster,或者是redis master-slave架构的主从异步复制导致的redis分布式锁的最大缺陷:在redis master实例宕机的时候,可能导致多个客户端同时完成加锁。

-解读源码-

知其然并知其所以然

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200225A05CH900?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券