关于redis:大军闲聊-Redisson和curator的分布式锁

54次阅读

共计 2016 个字符,预计需要花费 6 分钟才能阅读完成。

Redisson 的分布式锁是基于 redis 的,curator 的分布式锁是基于 zookeeper 的。
curator 的分布式锁在 zookeeper 之分布式锁曾经解说过了,这篇不讲源码的具体实现,讲讲他们实现分布式锁的流程。

Redisson 的分布式锁

假如当初有某个服务的线程 A,向 Redis 申请锁。他须要提供这个锁的名称(假如 lock)、本人的信息(假如 Thread-A)以及 key 生效的工夫(假如 30s,如果没有设置生效工夫,当线程 A 异样退出,这个 key 会始终保留在 Redis 中,那其余线程申请锁的时候,会始终阻塞导致申请不到锁)。

向 Redis 申请锁的时候,线程 A 会发送 Lua 脚本给 Redis。

因为思考到锁的可重入,所以这个数据结构是哈希,key 对应的值是锁的名称,哈希是锁的持有者和拿到锁的数值。

Redis 收到申请后,就会判断是否有这个 key,因为线程 A 是第一次申请这个 key 的,所以很显著没有,于是就创立新的哈希表,设置对应的数值和过期工夫。

如果线程 A 再申请 lock 这个锁,Redis 此时曾经有了这个 key,所以他就会看看这个 key 的持有者是不是以后申请者,此时以后持有者就是线程 A,所以就会把数值加 1,从 1 变成了 2,而后再重置过期工夫。

咱们下面设置了 30s 的过期工夫,如果程序执行了 30s,还没执行完,就过期了,那这个锁就生效了,其余线程就会拿到锁,导致互斥性生效,所以 Redisson 有一个监听狗的机制。

咱们的过期工夫是 30s,那监听狗(其实就是开启了一个线程)每 30s/3=10s 会给 Redis 发送申请,如果这个 key 的持有者是以后线程,那就会把生效工夫重置为 30s。

在线程 A 持有锁的时候,线程 B 也来申请锁。

因为这个 key 存在,且不是线程 B 持有的锁,所以他就获取锁失败了。

失败后他就会去订阅这个锁的解锁播送信息,并且创立信号量。

线程 A 开释锁的时候,会判断是否为以后锁的持有者,因为以后就是线程 A,所以把数值从 2 减 1,阐明还是线程 A 持有锁。

当线程 A 再开释锁的时候,这个数值就减为 0,阐明线程 A 曾经不须要这个锁了,此时 Redis 就会公布这个锁的解锁播送。

线程 B 可是始终订阅这个锁的解锁播送,当 A 解锁后,线程 B 就会接管到这个订阅,所以就开释信号量。

过后开释信号量的时候,线程 B 就开始从新申请,流程同上,如果还是没有获取到锁,那就会持续订阅这个锁的播送信息,当然不是有限期待,获取锁的过期工夫到了,就阐明没有获取锁。

curator 的分布式锁

同样假如当初有某个服务的线程 A,向 Zookeeper 申请锁。他须要提供这个锁的门路,并要求 Zookeeper 生成一个长期有序节点。因为这个 lock 目前还没有创立过长期节点,所以假如这个长期有序节点是 /lock/0001。Zookeeper 创立后就会把这个门路返回给线程 A。

线程 A 拿到门路后,还会去拿 /lock 下的所有节点(此时就 /lock0001),并进行排序。

排序完看看以后的节点是不是第一个,因为目前就是第一个,所以线程 A 就相当于拿到了锁,把线程的信息、节点的门路保留在内存中,并默认 lockCount 的值为 1。

如果线程 A 再申请锁,他就会先看看内存是否有以后线程和这个门路的信息,如果有,就把 lockCount 加 1,此时 lockCount 就是 2。

在线程 A 持有锁的时候,线程 B 也来申请锁。

因为这个 lock 曾经有了 0001 的长期有序节点,所以 Zookeeper 就会创立 lock0002 并返回给线程 B。

线程 B 拿到门路后,同样会去拿 /lock 下的所有节点(此时就有 /lock0001 和 /lock0002),并进行排序。

很显著他不是第一个节点,所以拿不到锁,于是就监听 lock0001,并进入了 wait。

线程 A 开释锁的时候,就会把内存中的 lockCount 减 1,而后判断 lockCount 是否大于 0,如果大于 0,阐明之前重入过,还须要持有锁,如果小于 0,阐明曾经开释完了,如果等于 0,阐明不须要加锁了,那就移除监听,并删除长期有序节点。

Zookeeper 删除 lock0001 时,Zookeeper 的 watch 机制就会告诉到线程 B,此时就会唤醒线程 B,线程 B 会从新去 Zookeeper 拉取子节点列表,并排序,此时 lock0002 是第一个,所以他就获取到了锁,而后把线程的信息、节点的门路保留在内存中,并默认 lockCount 的值为 1。

Zookeeper 分布式锁的流程大略如上。

那为什么是长期节点?当线程 A 异样退出后,这个长期节点也会删除,这样就能够通过 watch 机制告诉监听这个节点的服务。

为什么是程序节点?长期节点也能够保障互斥性,然而这样就会很多个服务始终争着去创立这个节点。

所以 Zookeeper 的长期节点,能够不必像 Redis 一样,须要过期工夫防死锁,Watch 机制不必像 Redis 一样,须要监听狗来放弃锁。

重入也有不一样的中央,Zookeeper 是保留客户端,Redis 是保留在 Redis 中。

正文完
 0