系统城装机大师 - 固镇县祥瑞电脑科技销售部宣传站!

当前位置:首页 > 系统教程 > Linux教程 > 详细页面

使用Redisson实现RedLock原理

时间:2020-03-21来源:电脑系统城作者:电脑系统城

前言

前面已经学习了Redission可重入锁以及公平锁的原理,接着看看Redission是如何来实现RedLock的。

RedLock原理

RedLock是基于redis实现的分布式锁,它能够保证以下特性:

  • 互斥性:在任何时候,只能有一个客户端能够持有锁;避免死锁:
  • 当客户端拿到锁后,即使发生了网络分区或者客户端宕机,也不会发生死锁;(利用key的存活时间)
  • 容错性:只要多数节点的redis实例正常运行,就能够对外提供服务,加锁或者释放锁;

RedLock算法思想,意思是不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,n / 2 + 1,必须在大多数redis节点上都成功创建锁,才能算这个整体的RedLock加锁成功,避免说仅仅在一个redis实例上加锁而带来的问题。

这里附上一个前几天对RedLock解析比较透彻的文章:
https://mp.weixin.qq.com/s/gOYWLg3xYt4OhS46woN_Lg

Redisson实现原理

Redisson中有一个MultiLock的概念,可以将多个锁合并为一个大锁,对一个大锁进行统一的申请加锁以及释放锁

而Redisson中实现RedLock就是基于MultiLock 去做的,接下来就具体看看对应的实现吧

RedLock使用案例

先看下官方的代码使用:
(https://github.com/redisson/redisson/wiki/8.-distributed-locks-and-synchronizers#84-redlock)

 1RLock lock1 = redisson1.getLock("lock1");
 2RLock lock2 = redisson2.getLock("lock2");
 3RLock lock3 = redisson3.getLock("lock3");
 4
 5RLock redLock = anyRedisson.getRedLock(lock1, lock2, lock3);
 6
 7// traditional lock method
 8redLock.lock();
 9
10// or acquire lock and automatically unlock it after 10 seconds
11redLock.lock(10, TimeUnit.SECONDS);
12
13// or wait for lock aquisition up to 100 seconds 
14// and automatically unlock it after 10 seconds
15boolean res = redLock.tryLock(100, 10, TimeUnit.SECONDS);
16if (res) {
17   try {
18     ...
19   } finally {
20       redLock.unlock();
21   }
22}

这里是分别对3个redis实例加锁,然后获取一个最后的加锁结果。

RedissonRedLock实现原理

上面示例中使用redLock.lock()或者tryLock()最终都是执行RedissonRedLock中方法。

RedissonRedLock 继承自RedissonMultiLock, 实现了其中的一些方法:

 1public class RedissonRedLock extends RedissonMultiLock {
 2    public RedissonRedLock(RLock... locks) {
 3        super(locks);
 4    }
 5
 6    /**
 7     * 锁可以失败的次数,锁的数量-锁成功客户端最小的数量
 8     */
 9    @Override
10    protected int failedLocksLimit() {
11        return locks.size() - minLocksAmount(locks);
12    }
13
14    /**
15     * 锁的数量 / 2 + 1,例如有3个客户端加锁,那么最少需要2个客户端加锁成功
16     */
17    protected int minLocksAmount(final List<RLock> locks) {
18        return locks.size()/2 + 1;
19    }
20
21    /** 
22     * 计算多个客户端一起加锁的超时时间,每个客户端的等待时间
23     * remainTime默认为4.5s
24     */
25    @Override
26    protected long calcLockWaitTime(long remainTime) {
27        return Math.max(remainTime / locks.size(), 1);
28    }
29
30    @Override
31    public void unlock() {
32        unlockInner(locks);
33    }
34
35}

看到locks.size()/2 + 1 ,例如我们有3个客户端实例,那么最少2个实例加锁成功才算分布式锁加锁成功。

接着我们看下lock()的具体实现

RedissonMultiLock实现原理

  1public class RedissonMultiLock implements Lock {
  2
  3    final List<RLock> locks = new ArrayList<RLock>();
  4
  5    public RedissonMultiLock(RLock... locks) {
  6        if (locks.length == 0) {
  7            throw new IllegalArgumentException("Lock objects are not defined");
  8        }
  9        this.locks.addAll(Arrays.asList(locks));
 10    }
 11
 12    public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
 13        long newLeaseTime = -1;
 14        if (leaseTime != -1) {
 15            // 如果等待时间设置了,那么将等待时间 * 2
 16            newLeaseTime = unit.toMillis(waitTime)*2;
 17        }
 18
 19        // time为当前时间戳
 20        long time = System.currentTimeMillis();
 21        long remainTime = -1;
 22        if (waitTime != -1) {
 23            remainTime = unit.toMillis(waitTime);
 24        }
 25        // 计算锁的等待时间,RedLock中:如果remainTime=-1,那么lockWaitTime为1
 26        long lockWaitTime = calcLockWaitTime(remainTime);
 27
 28        // RedLock中failedLocksLimit即为n/2 + 1
 29        int failedLocksLimit = failedLocksLimit();
 30        List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());
 31        // 循环每个redis客户端,去获取锁
 32        for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
 33            RLock lock = iterator.next();
 34            boolean lockAcquired;
 35            try {
 36                // 调用tryLock方法去获取锁,如果获取锁成功,则lockAcquired=true
 37                if (waitTime == -1 && leaseTime == -1) {
 38                    lockAcquired = lock.tryLock();
 39                } else {
 40                    long awaitTime = Math.min(lockWaitTime, remainTime);
 41                    lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
 42                }
 43            } catch (Exception e) {
 44                lockAcquired = false;
 45            }
 46
 47            // 如果获取锁成功,将锁加入到list集合中
 48            if (lockAcquired) {
 49                acquiredLocks.add(lock);
 50            } else {
 51                // 如果获取锁失败,判断失败次数是否等于失败的限制次数
 52                // 比如,3个redis客户端,最多只能失败1次
 53                // 这里locks.size = 3, 3-x=1,说明只要成功了2次就可以直接break掉循环
 54                if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
 55                    break;
 56                }
 57
 58                // 如果最大失败次数等于0
 59                if (failedLocksLimit == 0) {
 60                    // 释放所有的锁,RedLock加锁失败
 61                    unlockInner(acquiredLocks);
 62                    if (waitTime == -1 && leaseTime == -1) {
 63                        return false;
 64                    }
 65                    failedLocksLimit = failedLocksLimit();
 66                    acquiredLocks.clear();
 67                    // 重置迭代器 重试再次获取锁
 68                    while (iterator.hasPrevious()) {
 69                        iterator.previous();
 70                    }
 71                } else {
 72                    // 失败的限制次数减一
 73                    // 比如3个redis实例,最大的限制次数是1,如果遍历第一个redis实例,失败了,那么failedLocksLimit会减成0
 74                    // 如果failedLocksLimit就会走上面的if逻辑,释放所有的锁,然后返回false
 75                    failedLocksLimit--;
 76                }
 77            }
 78
 79            if (remainTime != -1) {
 80                remainTime -= (System.currentTimeMillis() - time);
 81                time = System.currentTimeMillis();
 82                if (remainTime <= 0) {
 83                    unlockInner(acquiredLocks);
 84                    return false;
 85                }
 86            }
 87        }
 88
 89        if (leaseTime != -1) {
 90            List<RFuture<Boolean>> futures = new ArrayList<RFuture<Boolean>>(acquiredLocks.size());
 91            for (RLock rLock : acquiredLocks) {
 92                RFuture<Boolean> future = rLock.expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
 93                futures.add(future);
 94            }
 95
 96            for (RFuture<Boolean> rFuture : futures) {
 97                rFuture.syncUninterruptibly();
 98            }
 99        }
100
101        return true;
102    }
103}

核心代码都已经加了注释,实现原理其实很简单,基于RedLock思想,遍历所有的Redis客户端,然后依次加锁,最后统计成功的次数来判断是否加锁成功。

分享到:

相关信息

  • linux 文件权限怎么解析

    常用权限linux系统内有档案有三种身份 u:拥有者 g:群组 o:其他人这些身份对于文档常用的有下面权限:r:读权限,用户可以读取文档的内容,如用cat,more查看w:写权限,用户可以编辑文档x...

    2024-07-07

  • 如何使用WPSeku找出 WordPress 安全问题?

    然而,如果我们遵循通常的 WordPress 最佳实践,这些安全问题可以避免。在本篇中,我们会向你展示如何使用 WPSeku,一个 Linux 中的 WordPress 漏洞扫描器,它可以被用来找出你安装...

    2024-07-03

系统教程栏目

栏目热门教程

人气教程排行

站长推荐

热门系统下载