实现分布式锁的处理。事实上这类琐最大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:
在Redis的Master节点上拿到了锁,但是这个加锁的key还没有同步到Slave节点,Master故障,发生故障转移,Slave节点升级为Master节点,导致锁丢失。
关于Redis实现分布式锁,Redis官方推荐使用Redlock。里面提到很多的有许多库和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都使用不同的方法,并且许多库使用简单的方法,与稍微复杂的方法相比可以实现更低的保证设计。故提供一种更规范的算法来使用Redis实现分布式锁。简称Redlock的算法。(
一、Redlock实现原理
在算法的分布式版本中,我们假设我们有N个Redis主机。这些节点完全独立,因此我们不使用复制或任何其他隐式协调系统。我们已经描述了如何在单个实例中安全地获取和释放锁。我们理所当然地认为算法将使用此方法在单个实例中获取和释放锁。在我们的示例中,我们设置N = 5,这是一个合理的值,因此我们需要在不同的计算机或虚拟机上运行5个Redis主服务器,以确保它们以大多数独立的方式失败。
为了获取锁,客户端执行以下操作:
- 它以毫秒为单位获取当前时间。
- 它尝试按顺序获取所有N个实例中的锁,在所有实例中使用相同的键名和随机值。在步骤2期间,当在每个实例中设置锁定时,客户端使用与总锁定自动释放时间相比较小的超时以获取它。例如,如果自动释放时间是10秒,则超时可以在~5-50毫秒范围内。这可以防止客户端长时间保持阻塞,试图与Redis节点进行通信,如果实例不可用,我们应该尝试尽快与下一个实例通话。
- 客户端通过从当前时间中减去在步骤1中获得的时间戳来计算获取锁定所需的时间。当且仅当客户端能够在大多数实例中获取锁定时(至少3个)并且获取锁定所经过的总时间小于锁定有效时间,认为锁定被获取。
- 如果获得了锁,则其有效时间被认为是初始有效时间减去经过的时间,如步骤3中计算的那样。
- 如果客户端由于某种原因(无法锁定N / 2 +
1实例或有效时间为负)未能获取锁定,它将尝试解锁所有实例(即使它认为不是能够锁定)。
二、Redisson 实现方式
maven依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.7</version>
</dependency>
github地址:https://github.com/redisson/redisson
代码实现
Config config1=new Config();
config1.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson1 = Redisson.create(config1);
Config config2=new Config();
config2.useSingleServer().setAddress("redis://127.0.0.1:6378");
RedissonClient redisson2 = Redisson.create(config2);
Config config3=new Config();
config3.useSingleServer().setAddress("redis://127.0.0.1:6377");
RedissonClient redisson3 = Redisson.create(config3);
RLock rLock1=redisson1.getLock("key");
RLock rLock2=redisson2.getLock("key");
RLock rLock3=redisson3.getLock("key");
// 向3个redis实例尝试加锁
RedissonRedLock redLock = new RedissonRedLock(rLock1,rLock2,rLock3);
boolean isLock;
try {
// 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。
isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);
System.out.println("isLock = "+isLock);
if (isLock) {
RedissonRedLock redLock2= new RedissonRedLock(rLock1,rLock2,rLock3);
isLock=redLock2.tryLock(500, 10000, TimeUnit.MILLISECONDS);
System.out.println("isLock = "+isLock);
redLock2.unlock();
}
} catch (Exception e) {
} finally {
// 无论如何, 最后都要解锁
redLock.unlock();
}
注:这里是作最简单的实现,更多示例可以查看git相关示例。