基于Zookeeper的分布式锁原理

目录

一、Zookeeper 的应用场景

1、分布式锁

利用 Zookeeper 的临时顺序节点,可以轻松实现分布式锁。

2、服务注册与发现

利用 ZnodeWatcher,可以实现分布式服务的注册和发现。

最著名的应用就是阿里的分布式 RPC 框架 Dubbo

3、共享配置和状态信息

Redis 的分布式解决方案 Codis,就是利用了 Zookeeper 来存放 数据路由表 和 codis-proxy 节点的元信息。同时 codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy。

此外,Kafka、HBase、Hadoop,也都依靠Zookeeper同步节点信息,实现高可用。

引自百度百科:

Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务。

二、什么是临时顺序节点?

Zookeeper 的分布式锁是由 “临时顺序节点” 的特性来实现的。

Zookeeper 的数据结构是树形的,这棵树由多个 Znode 节点组成。

Znode 分为以下四个类型:

1、持久节点(PERSISTENT)

默认的节点类型。创建节点的客户端与 Zookeeper 断开连接后,该节点依旧存在。

2、持久节点顺序节点(PERSISTENT_SEQUENTIAL)

所谓顺序节点,就是在创建节点时,Zookeeper 根据创建的时间顺序为该节点编号,如下图

image.png

3、临时节点(EPHEMERAL)

和持久节点相反,临时节点会在客户端与 Zookeeper 断开连接后被删除。

如下图所示,客户端与服务端建立连接后,自动创建一个临时节点

image.png

当客户端断开连接后,之前创建的临时节点也会被销毁

image.png

4、临时顺序节点(EPHEMERAL_SEQUENTIAL)

临时顺序节点 结合了 “临时节点” 和 “顺序节点” 的特性:在创建节点时 Zookeeper 根据创建的时间顺序给该节点进行编号,并根据该编号的顺序去分配临界资源的访问顺序,当创建节点的客户端与 Zookeeper 断开连接后,临时节点也会被删除。

三、Zookeeper分布式锁的原理

Zookeeper 分布式锁恰恰应用了临时顺序节点的特性,具体实现的步骤如下

0x01:获取锁

首先,在 Zookeeper 当中创建一个持久节点 ParentLock。 当第一个 Client 接入后想要获取锁时,需要在 ParentLock 这个节点下创建一个 临时顺序节点,这里我们称为 Lock1。如下图所示

image.png

在这之后,Client1 查找 ParentLock 下面的所有顺序节点并根据其序号进行排序,判断自己所创建的节点 Lock1 是否为第一个顺序节点,如果是则成功获取锁。

image.png

这时候,如果再有一个 Client2 接入,则会在 ParentLock 下再创建一个临时顺序节点 Lock2

image.png

Client2 查找 ParentLock 下面所有的临时顺序节点并进行排序,判断自己所创建的节点 Lock2 是不是顺序最靠前的一个,结果发现节点 Lock2 并不是最小的。

于是,Client2 开始向它前一位的顺序节点 Lock1 注册 Watcher ,用于监听 Lock1 节点的状态。这意味着 Client2 抢锁失败,进入到等待状态。

image.png

这时候,如果又有一个 Client3 接入并获取了锁,并重复了 Client2 的操作,向 Lock2 注册 Watcher

image.png

这样一来,Client1 获得了锁,Client2 监听了 Lock1 , Client3 监听了 Lock2 。形成了一个等待队列。

0x02:释放锁

释放锁分为两种情况:

情况1:任务完成,客户端显式的释放锁

当任务完成时,client 1 会显式的调用删除节点 Lock1 的指令,将其创建的节点删除。

image.png

情况2:任务执行过程中,客户端崩溃

获取锁的 Client1 在任务执行过程中如果出现崩溃,则会断开与 Zookeeper 服务端的连接,根据临时节点的特性,与之相关联的节点 Lock1 也会随之删除。

image.png

此时由于 Client2 一致监听着 Lock1 的存在状态,当 Lock1 节点被删除后,Client2 会立即收到通知,这时候 Client2 会再次查询 ParentLock 下面的所有节点,确认自己创建的节点 Lock2 是不是当前最小接地那,如果是,则 Lock2 当前的持有者 Client2 获得了锁。

image.png

同理,如果 Client2 也因为任务完成 或者 节点崩溃而删除了节点 Lock2,那么 Client3 就会接到通知。

0x03:Zookeeper 和 Redis 分布式锁的比较

分布式锁优点缺点
Zookeeper1、有封装好的框架,容易实现
2、有等待锁的队列,大幅度的提高了抢锁的效率
添加和删除节点的性能较低
RediSet 和 Del 指令的性能较高1、实现复杂,需要在代码层面考虑超时、原子性、误删等情况。
2、没有等待锁的队列,只能在客户端自旋来等待锁,效率低下。

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://codeyee.com/archives/dubbo-zk-3.html