算法思想
获得当前时间(ms)
首先设置一个锁有效时间valid_time,也就是超过这个时间后锁自动释放,使用相同的key和value对所有redis实例进行设置,每次链接redis实例时设置一个小于valid_time的超时时间,比如valid_time时10s,那超时时间可以设置成50ms,如果这个实例不行,那么换下一个设置
计算获取锁总共占用的时间,再加上时钟偏移,如果这个总时间小于valid_time,并且成功设置锁的实例数>= N/2 + 1,那么加锁成功
直白点说,就是采用多个个独立的redis节点,同时setnx,如果多数节点成功,就拿到了锁,这样就可以允许少数节点挂掉了。整个取锁、释放锁的操作和单节点类似
Redisson实现案例(代码来着官方GitHub)
基于Redis的Redisson红锁
RedissonRedLock
对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock
对象关联为一个红锁,每个RLock
对象实例可以来自于不同的Redisson实例。RLock lock1 = redissonInstance1.getLock("lock1"); RLock lock2 = redissonInstance2.getLock("lock2"); RLock lock3 = redissonInstance3.getLock("lock3"); RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); // 同时加锁:lock1 lock2 lock3 // 红锁在大部分节点上加锁成功就算成功。 lock.lock(); ... lock.unlock();大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了
leaseTime
的参数来指定加锁的时间。超过这个时间后锁便自动解开了。RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3); // 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开 lock.lock(10, TimeUnit.SECONDS); // 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); ... lock.unlock();
使用方式
pom引入
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.13.0</version> </dependency>
简单栗子
public class RedissonConfig { static Config config = new Config(); static { config.useSingleServer() //config.useReplicatedServers() // .setScanInterval(2000) // 主节点变化扫描间隔时间 //可以用"rediss://"来启用SSL连接 .setAddress("redis://127.0.0.1:6379"); } RedissonClient redisson;public static void main(String[] args) { RedissonClient redissonClient = new RedissonConfig().getRedisson(); RLock lock1 = redissonClient.getLock("test"); RedissonRedLock lock = new RedissonRedLock(lock1); // 加锁,如果没有手动解开的话,10秒钟后将会自动解开 lock.lock(10, TimeUnit.SECONDS); try { // 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if(!res){ System.out.println("枷锁失败"); return; } 执行业务(需要锁定的部分) System.out.println("业务代码"); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } }public RedissonConfig(){ redisson = Redisson.create(config); } public static Config getConfig() { return config; } public static void setConfig(Config config) { RedissonConfig.config = config; } public RedissonClient getRedisson() { return redisson; } public void setRedisson(RedissonClient redisson) { this.redisson = redisson; } }