Redisson
概念
Redisson入门
测试使用Redisson
使用Redisson
trylock时不传参数表示只尝试一次,也就是不重试了,也可以传参数指定要重试多久
TODO Redisson的分布式功能不用考虑uuid了?不用担心阻塞导致释放别人的锁?释放锁时不用判断是自己的锁?
Redisson可重入原理与实现
对于我们之前自定义的基于Redis实现的分布式锁,实际上是通过setnx实现的,每次尝试获取锁时都会执行setnx,而自定义锁在获取一次锁之后执行的业务里又再一次获取锁,由于nx参数是互斥的,所以会导致获取锁失败,即不可重入,导致阻塞
reentrantlock是一种可重入锁,它的实现原理是:每次获取锁时判断当前锁的获取对象是不是自己(之前分布式锁的做法是,通过唯一标识判断锁的获取对象是不是自己从而可以准确释放锁,无法重入获取锁,现在是想要用redisson的方法实现根据获取对象重入式地获取锁、释放锁),如果是则获取成功,同时记录获取锁的次数,即需要增加锁的线程标识、获取锁次数
使用hash结构存储两个键值对:field---锁的线程标识、value---获取锁次数
由于hash结构没有nx、ex这样的组合参数,所以需要自己实现---获取锁并添加线程标识、设置有效期
当内部调用方法里的业务执行完后不会释放锁,而是让获取锁次数减一,直到获取锁次数为0了才会真正释放锁
使用lua脚本实现可重入
Redisson可重试原理与实现
只传一个waitTime,会默认给leasetime(超时释放时间)置为-1
继续往下,尝试获取锁tryAcquire
继续往下,调用tryAcquireAsync
继续往下,由于leasetime为-1,跳过if,执行tryLockInnerAsync,其中把超时释放时间默认设置为getLockWatchdogTimeout(看门狗)30秒
继续往下,执行lua脚本,其中脚本中有两个if,第一个if是判断是否存在锁,如果不存在,则获取锁,且次数+1,如果存在,就判断是否是自己之前获取过该锁,如果是,则次数+1即可,因此在这段逻辑中,获取锁成功返回nil(null),获取锁失败返回pttl(超时释放时间,以毫秒为单位)
往回看,得到的pttl封装在RFuture中,同时这个方法返回的也是一个Future
往回看,就是把有效期一直向上返回,这里是返回的是Long类型
往回看,如果从底层返回来的有效期是null(nil),说明获取锁成功,否则获取锁失败,此时有效期已经消耗一段时间了,需要减掉,如果刚好超过有效期限定时长,则说明整个过程获取锁失败
还是这个函数,只剩下一种情况,刚刚获取锁失败且未过有效期,那没必要立马就再去获取锁,可以订阅(subscribe)一个消息,当有线程释放锁时会接收到消息,从而再尝试获取锁,至于释放锁的消息是如何发送的,在以下lua脚本中的publish来实现,否则,如果已经超时了,就会取消订阅(unsubscribe)
还是这个函数,继续执行,首先通过判断订阅等待时是否超时,如果确实在有效期内等到别的线程释放锁了,则可以开始重试(再次尝试获取锁),逻辑同前面一样,这里的ttl(施放时间leasetime还是默认的30秒)
还是剩下一种情况,获取锁失败且未过有效期,此时换了一种方式(信号量机制)来等待重试,分为两种情况,只要在时间较小的那个时段内尝试获取锁即可
如果还是获取失败,再次判断剩余等待时间还够不够,如果不够就整个过程获取锁失败,如果还够,继续执行while循环,尝试获取锁
Redisson超时释放原理与实现
TODO
可重试与超时释放的流程图
Redisson主从一致性原理与实现
主从一致性问题
一旦主节点宕机,如果只有一个主从,则锁失效
如果有多个主从(称为联锁),可从未宕机的主节点进行查询
解决主从一致性问题:使用multiLock
配置三个节点
创建联锁