上一期介绍了springboot整合redis实现分布式锁的demo,如何在项目里面集成,这一期我们从源码的角度来看看sprigboot到底是如何整合redis实现分布式锁的
在上一期的demo中,我们在bean中定义了一个RedisLockRegistry
我们一起来分析下这个类,首先跟踪到源码,看他的构造方法
我们主要关注这行代码
OBTAIN_LOCK_SCRIPT定义了一段lua脚本
这段lua脚本的大致意思是
获取传进来的key的值,如果lockCLientId的值和传进来的key对应的值相等,则为这个key设置存活时间,
如果传进来的值和lockCLientId不相等,则为这个key设置毫秒值
大致解释了下这段脚本,如果又不对的地方,大家可以提出来,拜谢
再说说lua脚本
Redis 脚本使用单个Lua 解释器来执行脚本,并且Redis 也保证脚本会以原子性(atomic)的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。这和使用 MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effect)要么是不可见的(not visible),要么就是已完成的(already completed)
所以使用者这段lua脚本来保证原子性再好不过了而且
使用lua脚本还可以
减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延;
原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。
接下来,当初始化完成之后,就需要去获取锁了,我们看看obtain这个方法
这里定义的locks是一个ConcurrentHashMap
若key对应的value为空,会将第二个参数的返回值存入并返回,
接下来,跟着原始来看看redisLock
定义额一个内部类,实现了lock接口,接着下一步,看看trylock
首先定义一个当前系统时间,
第二步:获取过期时间expire
第三步执行obtinLock方法
redis执行execute方法
obtainLockScript:之前定义的lua脚本
lockKey:传进来的ksy
clientId:随机字符串
expireAfter:过期时间
我们一起去看看exeute这个方法
这里多提一句这个写法Object... args
J2SE 1.5提供了“Varargs”机制。借助这一机制,可以定义能和多个实参相匹配的形参,当你的传入参数特别多的时候,你在接收的时候就可以使用这样的方式,就是一种简写,但是会感高大上有没有,哈哈,真的是要多看源码啊
我们进入scriptExecuter的executer的实现类
这段代码的大致意思就是获取我们传递进来的参数,然后将参数赋值给定义的那段lua脚本
接下来,调用eval方法执行这段lua脚本,这里是带着参数的,有不明白的同学可以看下redia的lua脚本,这里就是使用java代码赋值参数,然后执行,和在redis里面直接执行lua脚本一个意思
接下来,回到上面,
将执行完脚本的结果赋值给acquired,如果acquired为true,就是拿到了锁,就开始执行咱们自己的业务逻辑.
由于本人也是目前正在慢慢看源码,只是写下了自己的一些总结,可能会有很多错误或者不对的地方,还请大家指出,Thanks!
2019年8月19号