大气wordpress主题,河南网络优化服务,下载wap浏览器,招标网官网下载redisson
redisson如何来进行redis分布式锁实现的源码#xff0c;基于redis实现各种各样的分布式锁的原理 https://redisson.org/ 这是官网 https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档
开始 demo
建一个普通的工程在pom.xml里引入依赖
基于redis实现各种各样的分布式锁的原理 https://redisson.org/ 这是官网 https://github.com/redisson/redisson/wiki/Table-of-Content 这是官方文档
开始 demo
建一个普通的工程在pom.xml里引入依赖
dependency
groupIdorg.redisson/groupId
artifactIdredisson/artifactId
version3.8.1/version
/dependency 参照官网构建RedissonClient同时看看对应的配置 由于要在测试多节点所以我再两台虚拟机配置了一下redis cluster集群标准的三主三从配置。
Config config new Config();
config.useClusterServers()
.addNodeAddress(redis://192.168.1.1:7001)
.addNodeAddress(redis://192.168.1.1:7002)
.addNodeAddress(redis://192.168.1.1:7003)
.addNodeAddress(redis://192.168.2.2:7001)
.addNodeAddress(redis://192.168.2.2:7002)
.addNodeAddress(redis://192.168.2.2:7003);RedissonClient redisson Redisson.create(config);简单用一下分布式锁的功能
RLock lock redisson.getLock(anyLock);
lock.lock();
lock.unlock();RMapString, Object map redisson.getMap(anyMap);
map.put(foo, bar); map redisson.getMap(anyMap);
System.out.println(map.get(foo)); getLock()
点进getLock方法可以看到如下
Override
public RLock getLock(String name) {
return new RedissonLock(connectionManager.getCommandExecutor(), name);
}getLock()方法的时候获取到的Lock对象是RedissonLock对象就可以了里面封装了一个ConnectionManager里获取的一个CommandExecutorCommandExecutor是什么东西 既然是Connection开头的里面一定是封装了一个跟redis之间进行通信的一个Cconnection连接对象CommandExecutor命令执行器封装了一个redis连接的命令执行器可以执行一些set、get redis的一些操作用来执行底层的redis命令的
RedissonLock
在这个RedissonLock的构造函数里面建议大家关注的一行代码别的没什么主要是一个internalLockLeaseTime的东西跟watchdog看门狗有关系的 我们在RedissonLock里面打几个断点 还有一个是lock()和unlock()方法既然调用了肯定要看一下
再往后走到这里可以看到在默认情况下加锁的时候long leaseTime, TimeUnit unit是没有的-1和null就代表着说只要你加到了一把锁就一定会永久性的持有这把锁除非是你当前持有这把锁的机器宕机了watchdog看门狗就会发现然后就会释放锁避免说永久性的一个死锁发生
再去下面142行看看tryAcquire()做了什么 这个里面的方法tryLockInnerAsync() 这个是核心
tryLockInnerAsync() 加锁lua脚本
先仔细看看这里面的一块绿色代码明显不是java其实这就是redis的lua脚本 来分析一下 if (redis.call(‘exists’, KEYS[1]) 0) thenKEYS[1]一看就是我们设置的那个锁的名字人家先执行了redis的exists的指令判断一下如果“anyLock”这个key不存在那么就进行加锁实际加锁的指令 redis.call(‘hset’, KEYS[1], ARGV[2], 1); redis.call(‘pexpire’, KEYS[1], ARGV[1]); return nil; hsetredis的一个指令相当于是在redis的一个map数据结构里设置一个key value 这个map对象的名字是我们传的anyLock然后里面的k-v键值对k我们假设为lockStatev值呢设置为了1 hset KEYS[1] ARGV[2] 1 转换我们传的指令后就是 hset anyLock lockState 1
pexpire KEYS[1] ARGV[1]设置一个key的过期时间 KEYS[1]其实可以理解为就是我们设置的那个key“anyLock”ARGV[1]其实就是一个这个key的过期时间可能是默认的一个值叫做30000毫秒30s很有可能是说的是这个anyLock这个key对应的过期时间就是30秒
再往下的逻辑if (redis.call(‘hexists’, KEYS[1], ARGV[2]) 1) then redis.call(‘hincrby’, KEYS[1], ARGV[2], 1); redis.call(‘pexpire’, KEYS[1], ARGV[1]); return nil; end; 判断名字为anyLock的这个map对象里面某个k-v键值对key键假设为lockState他的对应值是不是1上面的逻辑中可以看到如果存在的话就把这个值用命令hincrby 1 hincrby KEYS[1] ARGV[2] 1将anyLock这个map中的lockState这个key的值累加1 pexpire’, KEYS[1], ARGV[1]是又把这个对象设置了一下过期时间 最后的return redis.call(‘pttl’, KEYS[1]); 是返回当前还有多久就过期了
那总结一下这个加锁逻辑我们都知道 redis中的map数据结构是键值对。 那么这个加锁的时候就是先判断 我们定义的锁名称“anyLock” 这个map结构是否存在如果不存在的话就加一个默认的键值对 k-vkey某个默认值 value1 如果这个名为“anyLock”的map结构已经存在了就把那个默认的键值对k-vkey某个默认值 value1
commandExecutor.evalWriteAsync()
OK分析完脚本再点进这个方法来看看 因为现在用的是redis cluster3主3从的模式那么这里就有是要把key放在其中某一个master上去。 这里的第一行int slot connectionManager.calcSlot(key); 取出来redis cluster的数量我们都知道redis cluster集群的节点默认是16384个slot的数量默认就是16384个。 那这第一行的操作就是根据key的hash值来计算出这个key要落到应该要哪个slot节点上
下一行代码就很明显了是根据slot节点获取到这个slot是放在哪个master上的 anyLock这个key13434算出来是这个slot相当于是针对“anyLock”这个key计算出来一个hash值然后将这个hash值对16384这个slot数量进行取模int slot connectionManager.calcSlot(key); 取模之后就可以拿到当前这个“anyLock”的key对应的是哪个slot 再回到getNodeSource()方法中看看MasterSlaveEntry MasterSlaveEntry [masterEntry[freeSubscribeConnectionsAmount1, freeSubscribeConnectionsCounter50, freeConnectionsAmount32, freeConnectionsCounter64, freezedfalse, freezeReasonnull, client[addrredis://192.168.1.1:7002], nodeTypeMASTER, firstFail0]]
redis://192.168.1.1:7002编号为13434的slot所在的master是这台机器的这个端口对应的master实例 此时就是已经知道了其实必须是将加锁的那段lua脚本放到redis://192.168.1.1:7002这个master实例上去执行完成加锁的操作
回过头看一下这里的代码 这里的逻辑很简单了里面也没必要再去跟的很深了这里大概就是上面我们分析的拿着刚才生成的lua脚本去对应的master节点中执行脚本把数据存入。
总结
总结一下这里就是最基础的通过lua脚本加锁的逻辑我们知道了redis加锁的时候是通过lua脚本将其传到redis中来加锁的。加锁的时候本质上就是新建了一个map类型的数据key是我们的锁名称。
流程
判断key是否存在 不存在的话新建一个map类型的数据数据名称为锁名称map中的数据只有一对k-vkey应该为加锁次数默认value为1这个主要用来做同一个线程多次加锁的重入操作pexpire 命令设置过期时间默认为30000ms 也就是30s 存在的话 hincrby 命令将map中的加锁次数 1pexpire 再次将过期时间设置为30000ms 也就是30s pttl命令返回当前数据的过期时间