冷饭新炒:理解Redisson中分布式锁的实现
2021-06-04 10:01:23 1 举报
冷饭新炒:理解Redisson中分布式锁的实现
作者其他创作
大纲/内容
异步加锁,同步获取结果
订阅这个操作执行的时间、time和当期时间进行计算判断是否超时
HASH
pttl = 30000
加锁操作
执行加锁Lua脚本
成功结束
Redis服务
Redis
hincrby resource:x 559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:10087 1
客户端Y
2
失败结束
传入线程ID执行解锁的Lua脚本
hset resource:x 559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:10087 1
是
结束
如果返回ttl不为空,循环等待获取锁
leaseTime == -1
客户端B,KEY = resource:x,尝试加锁(不同进程,不同线程,,线程ID = 10001)
返回null意味着线程状态不一致(加锁和解锁的调用线程不一致)
进入死循环尝试获取锁
订阅异常
客户端A,KEY = resource:x,续期或者再次加锁(线程ID = 10087)
获取锁成功,跳出死循环
退订解锁事件(redisson_lock__channel:{$KEY})
resource:x
返回RFuture包装的ttl,如果ttl不存在说明加锁成功
执行加锁的Lua脚本,异步执行,同步获取结果
删除成功
waitTime转化为毫秒= time记录当前时间current
Lua脚本执行结果
线程ID指定为NULL取消续期任务
exists resource:x == 1 && hexists resource:x 559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:10088 == 0 => 这样就会返回pttl resource:x
加锁失败
559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:10087
订阅redisson_lock__channel:{RESOURCE}的解锁事件
不带waitTime参数的加锁流程
field
计算出来的最终结果决定采用ttl或者time进行阻塞等待
解锁操作
基于ttl、time、上一个时间戳和当前时间戳进行计算
死循环中阻塞等待重试
订阅操作没有超过剩余时限
强制解锁流程
获取订阅结果
直接锁所在的KEY
一般解锁流程
返回的Throwable实例不为NULL意味着脚本执行异常
否
锁KEY的过期时间重置为lockWatchdogTimeout
获取锁失败,返回锁KEY的ttl
1
退阅redisson_lock__channel:{RESOURCE}
加锁成功
value
首次加锁成功新建续期任务,续期任务周期为lockWatchdogTimeout/3
执行解锁Lua脚本
订阅解锁事件(redisson_lock__channel:{$KEY})
默认每隔10秒延长KEY的过期时间30秒
exists resource:x == 1 && hexists resource:x 559cc9df-bad8-4f6c-86a4-ffa51b111c36:10001 == 0 => 这样就会返回pttl resource:x
客户端X
客户端B,KEY = resource:x,尝试加锁(同一个进程,不同线程,线程ID = 10088)
向订阅渠道redisson_lock__channel:{RESOURCE}发布解锁事件
获取锁失败
获取锁成功
解锁Lua脚本执行完毕
超时失败
pexpire resource:x 30000
等待获取锁的时间超过剩余时限
返回的结果为1(true)
锁KEY的过期时间为leaseTime转化为毫秒
(try)Lock( )方法调用
订阅操作时间超过剩余时限
客户端线程阻塞等待,等待时间为ttl,可以根据入参决定是否支持中断
删除失败
这个脚本步骤比较多,这里不做流程分析,见源码分析部分
lock( )方法调用
周期性执行续期(看门狗)
计算当前时间与前面的current的时间差,与time判断是否超时
带有waitTime参数的加锁流程
没有超时
接收客户端X解锁事件,阻塞唤醒(redisson_lock__channel:{$KEY})
传入线程ID取消续期任务
客户端A,KEY = resource:x,首次加锁(线程ID = 10087)
0 条评论
下一页