Zookeeper分布式锁

前言

实现一把分布式锁通常有很多方法,比较常见的有 redis 和 Zookeeper。Zookeeper能实现分布式锁,是因为它有一个特性,就是多个线程去 Zookeeper 里面去创建同一个节点的时候,只会有一个线程执行成功。

Zookeeper 的 ZNode 节点

Zookeeper 里面的节点可以分为两大类,一种是临时节点,一种是持久化节点。

临时节点,指的是节点创建后,如果创建节点的客户端和 Zookeeper 服务端的会话失效(例如断开连接),那么节点就会被删除。

持久化节点指的是节点创建后,即使创建节点的客户端和 Zookeeper 服务端的会话失效(例如断开连接),节点也不会被删除,只有客户端主动发起删除节点的请求,节点才会被删除。

另外还有一种节点叫做有序节点,这种节点在创建时会有一个序号,这个序号是自增的。有序节点既可以是有序临时节点,也可以是有序持久化节点。

Zookeeper 的分布式锁实现

有两种方案:1. 基于临时节点实现;2. 基于临时顺序节点实现。第一种方案会有羊群效应,这里不做介绍。

临时顺序节点实现分布式锁

在每一个节点下面创建临时顺序节点(EPHEMERAL_SEQUENTIAL)类型,新的子节点后面,会加上一个次序编号,而这个生成的次序编号,是上一个生成的次序编号加一。

例如,有一个用于发号的节点“/test/lock”为父亲节点,可以在这个父节点下面创建相同前缀的临时顺序子节点,假定相同的前缀为“/test/lock/seq-”。第一个创建的子节点基本上应该为/test/lock/seq-0000000000,下一个节点则为/test/lock/seq-0000000001,依次类推。

在这里插入图片描述

一个ZooKeeper分布式锁,首先需要创建一个父节点,尽量是持久节点(PERSISTENT类型),然后每个要获得锁的线程,都在这个节点下创建个临时顺序节点。由于ZK节点,是按照创建的次序,依次递增的。

为了确保公平,可以简单的规定:编号最小的那个节点,表示获得了锁。所以,每个线程在尝试占用锁之前,首先判断自己是排号是不是当前最小,如果是,则获取锁,也就是谁先创建的节点,谁获取锁。

 ZooKeeper的节点监听机制,可以保障占有锁的传递有序而且高效。

每个线程抢占锁之前,先尝试创建自己的ZNode。同样,释放锁的时候,就需要删除创建的Znode。创建成功后,如果不是排号最小的节点,就处于等待通知的状态。等谁的通知呢?不需要其他人,只需要等前一个Znode的通知就可以了。前一个Znode删除的时候,会触发Znode事件,当前节点能监听到删除事件,就是轮到了自己占有锁的时候。第一个通知第二个、第二个通知第三个,击鼓传花似的依次向后。

ZooKeeper的节点监听机制,能够非常完美地实现这种击鼓传花似的信息传递。具体的方法是,每一个等通知的Znode节点,只需要监听(linsten)或者监视(watch)排号在自己前面那个,而且紧挨在自己前面的那个节点,就能收到其删除事件了。
只要上一个节点被删除了,就进行再一次判断,看看自己是不是序号最小的那个节点,如果是,自己就获得锁。

ZooKeeper的内部优越的机制,能保证由于网络异常或者其他原因,集群中占用锁的客户端失联时,锁能够被有效释放。一旦占用Znode锁的客户端与ZooKeeper集群服务器失去联系,这个临时Znode也将自动删除。排在它后面的那个节点,也能收到删除事件,从而获得锁。正是由于这个原因,在创建取号节点的时候,尽量创建临时znode节点。

分布式锁的抢占过程

客户端A发起一个加锁请求

在这里插入图片描述

客户端A发起一个加锁请求,会在你要加锁的node下添加一个临时顺序节点,这一大坨长长的名字都是Curator框架自己生成出来的。 

然后,那个最后一个数字是"1"。因为客户端A是第一个发起请求的,所以给他搞出来的顺序节点的序号是"1"。

接着客户端A创建完一个顺序节点。还没完,他会查一下"my_lock"这个锁节点下的所有子节点,并且这些子节点是按照序号排序的,这个时候他大概会拿到这么一个集合:

img

接着客户端A会走一个关键性的判断,这个集合里,我创建的那个顺序节点,是不是排在第一个啊?

如果是的话,那我就可以加锁了啊!因为明明我就是第一个来创建顺序节点的人,所以我就是第一个尝试加分布式锁的人啊!

bingo!加锁成功!大家看下面的图,再来直观的感受一下整个过程。

在这里插入图片描述

客户端B发起一个加锁请求 

客户端A都加完锁了,客户端B过来想要加锁了,这个时候他会干一样的事儿:先是在"my_lock"这个锁节点下创建一个临时顺序节点,此时名字会变成类似于:

img

在这里插入图片描述 

客户端B因为是第二个来创建顺序节点的,所以zk内部会维护序号为"2"。

接着客户端B会走加锁判断逻辑,查询"my_lock"锁节点下的所有子节点,按序号顺序排列,此时他看到的类似于:

img

同时检查自己创建的顺序节点,是不是集合中的第一个?

明显不是,此时第一个是客户端A创建的那个顺序节点,序号为"01"的那个。所以加锁失败!

客户端B开启监听客户端A

 加锁失败了以后,客户端B就会通过ZK的API对他的顺序节点的上一个顺序节点加一个监听器。监听上一个节点是否被删除等变化!

在这里插入图片描述

接着,客户端A加锁之后,可能处理了一些代码逻辑,然后就会释放锁。那么,释放锁是个什么过程呢?

其实很简单,就是把自己在zk里创建的那个顺序节点删除。删除了那个节点之后,zk会负责通知监听这个节点的监听器,也就是客户端B之前加的那个监听器,说:你监听的那个节点被删除了,有人释放了锁。

在这里插入图片描述

此时客户端B的监听器感知到了上一个顺序节点被删除,也就是排在他之前的某个客户端释放了锁。

客户端B抢锁成功 

集合里此时只有客户端B创建的唯一的一个顺序节点了!

然后呢,客户端B判断自己是集合中的第一个顺序节点,直接完成加锁,运行后续的业务代码即可,运行完了之后再次释放锁。

 在这里插入图片描述

这里仅对ZK分布式锁做一个抛砖引玉,具体实现可参考:

Zookeeper 分布式锁 (图解+秒懂+史上最全) - 疯狂创客圈 - 博客园文章很长,建议收藏起来慢慢读!疯狂创客圈总目录 语雀版 | 总目录 码云版| 总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 经典图书:《Java高并发核心编程(卷1)》 面试必备 + 大厂必https://www.cnblogs.com/crazymakercircle/p/14504520.html 

 

  • 7
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值