基于 Zookeeper 的分布式锁实现
1. 背景
最近在学习 Zookeeper,在刚开始接触 Zookeeper 的时候,完全不知道 Zookeeper 有什么用。且很多资料都是将 Zookeeper 描述成一个“类 Unix/Linux 文件系统”的中间件,导致我很难将类 Unix/Linux 文件系统的 Zookeeper 和分布式应用联系在一起。后来在粗读了《ZooKeeper 分布式过程协同技术详解》和《从Paxos到Zookeeper 分布式一致性原理与实践》两本书,并动手写了一些 CURD demo 后,初步对 Zookeeper 有了一定的了解。不过比较肤浅,为了进一步加深对 Zookeeper 的认识,我利用空闲时间编写了本篇文章对应的 demo – 基于 Zookeeper 的分布式锁实现。通过编写这个分布式锁 demo,使我对 Zookeeper 的 watcher 机制、Zookeeper 的用途等有了更进一步的认识。不过我所编写的分布式锁还是比较简陋的,实现的也不够优美,仅仅是个练习,仅供参考使用。好了,题外话就说到这里,接下来我们就来聊聊基于 Zookeeper 的分布式锁实现。
2. 独占锁和读写锁的实现
在本章,我将分别说明独占锁和读写锁详细的实现过程,并配以相应的流程图帮助大家了解实现的过程。这里先说说独占锁的实现。
2.1 独占锁的实现
独占锁又称排它锁,从字面意思上很容易理解他们的用途。即如果某个操作 O1 对访问资源 R1 的过程加锁,在操作 O1结束对资源 R1 访问前,其他操作不允许访问资源 R1。以上算是对独占锁的简单定义了,那么这段定义在 Zookeeper 的“类 Unix/Linux 文件系统”的结构中是怎样实现的呢?在锁答案前,我们先看张图:
图1 独占锁的 Zookeeper 节点结构
如上图,对于独占锁,我们可以将资源 R1 看做是 lock 节点,操作 O1 访问资源 R1 看做创建 lock 节点,释放资源 R1看做删除 lock 节点。这样我们就将独占锁的定义对应于具体的 Zookeeper 节点结构,通过创建 lock 节点获取锁,删除节点释放锁。详细的过程如下:
- 多个客户端竞争创建 lock 临时节点
- 其中某个客户端成功创建 lock 节点,其他客户端对 lock 节点设置 watcher
- 持有锁的客户端删除 lock 节点或该客户端崩溃,由 Zookeeper 删除 lock 节点
- 其他客户端获得 lock 节点被删除的通知
- 重复上述4个步骤,直至无客户端在等待获取锁了
上面即独占锁具体的实现步骤,理解起来并不复杂,这里不再赘述。
图2 获取独占锁流程图
2.2 读写锁的实现
说完独占锁的实现,这节来说说读写锁的实现。读写锁包含一个读锁和写锁,操作 O1 对资源 R1 加读锁,且获得了锁,其他操作可同时对资源 R1 设置读锁,进行共享读操作。如果操作 O1 对资源 R1 加写锁,且获得了锁,其他操作再对资源 R1 设置不同类型的锁都会被阻塞。总结来说,读锁具有共享性,而写锁具有排他性。那么在 Zookeeper 中,我们可以用怎样的节点结构实现上面的操作呢?
图3 读写锁的 Zookeeper 节点结构
在 Zookeeper 中,由于读写锁和独占锁的节点结构不同,读写锁的客户端不用再去竞争创建 lock 节点。所以在一开始,所有的客户端都会创建自己的锁节点。如果不出意外,所有的锁节点都能被创建成功,此时锁节点结构如图3所示。之后,客户端从 Zookeeper 端获取 /share_lock 下所有的子节点,并判断自己能否获取锁。如果客户端创建的是读锁节点,获取锁的条件(满足其中一个即可)如下:
- 自己创建的节点序号排在所有其他子节点前面
- 自己创建的节点前面无写锁节点
如果客户端创建的是写锁节点,由于写锁具有排他性。所以获取锁的条件要简单一些,只需确定自己创建的锁节点是否排在其他子节点前面即可。
不同于独占锁,读写锁的实现稍微复杂一下。读写锁有两种实现方式,各有异同,接下来就来说说这两种实现方式。
读写锁的第一种实现
第一种实现是对 /share_lock 节点设置 watcher,当 /share_lock 下的子节点被删除时,未获取锁的客户端收到 /share_lock 子节点变动的通知。在收到通知后,客户端重新判断自己创建的子节点是否可以获取锁,如果失败,再次等待通知。详细流程如下:
- 所有客户端创建自己的锁节点
- 从 Zookeeper 端获取 /share_lock 下所有的子节点,并对 /share_lock 节点设置 watcher
- 判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则继续等待
- 持有锁的客户端删除自己的锁节点,其他客户端收到 /share_lock 子节点变动的通知
- 重复步骤2、3、4,直至无客户端在等待获取锁了
上述步骤对于的流程图如下:
图4 获取读写锁实现1流程图
上面获取读写锁流程并不复杂,但却存在性能问题。以图3所示锁节点结构为例,第一个锁节点 host1-W-0000000001 被移除后,Zookeeper 会将 /share_lock 子节点变动的通知分发给所有的客户端。但实际上,该子节点变动通知除了能影响 host2-R-0000000002 节点对应的客户端外,分发给其他客户端则是在做无用功,因为其他客户端即使获取了通知也无法获取锁。所以这里需要做一些优化,优化措施是让客户端只在自己关心的节点被删除时,再去获取锁。
读写锁的第二种实现
在了解读写锁第一种实现的弊端后,我们针对这一实现进行优化。这里客户端不再对 /share_lock 节点进行监视,而只对自己关心的节点进行监视。还是以图3的锁节点结构进行举例说明,host2-R-0000000002 对应的客户端 C2 只需监视 host1-W-0000000001 节点是否被删除即可。而 host3-W-0000000003 对应的客户端 C3 只需监视 host2-R-0000000002 节点是否被删除即可,只有 host2-R-0000000002 节点被删除,客户端 C3 才能获取锁。而 host1-W-0000000001 节点被删除时,产生的通知对于客户端 C3 来说是无用的,即使客户端 C3 响应了通知也没法获取锁。这里总结一下,不同客户端关心的锁节点是不同的。如果客户端创建的是读锁节点,那么客户端只需找出比读锁节点序号小的最后一个的写锁节点,并设置 watcher 即可。而如果是写锁节点,则更简单,客户端仅需对该节点的上一个节点设置 watcher 即可。详细的流程如下:
- 所有客户端创建自己的锁节点
- 从 Zookeeper 端获取 /share_lock 下所有的子节点
- 判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则对自己关心的锁节点设置 watcher
- 持有锁的客户端删除自己的锁节点,某个客户端收到该节点被删除的通知,并获取锁
- 重复步骤4,直至无客户端在等待获取锁了
上述步骤对于的流程图如下:
图5 获取读写锁实现2流程图
3. 写在最后
本文较为详细的描述了基于 Zookeeper 分布式锁的实现过程,并根据上面描述的两种锁原理实现了较为简单的分布式锁 demo,代码放在了 github 上,需要的朋友自取。因为这只是一个简单的 demo,代码实现的并不优美,仅供参考。最后,如果你觉得文章还不错的话,欢迎点赞。如果有不妥的地方,也请提出来,我会虚心改之。好了,最后祝大家生活愉快,再见。
参考
- 《ZooKeeper 分布式过程协同技术详解》
- 《从Paxos到Zookeeper 分布式一致性原理与实践》
- 本文链接: https://www.tianxiaobo.com/2018/01/20/基于-Zookeeper-的分布式锁实现/
from:http://www.tianxiaobo.com/2018/01/20/%E5%9F%BA%E4%BA%8E-Zookeeper-%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/
基于 Zookeeper 的分布式锁实现相关推荐
- ieee39节点系统介绍_Java秒杀系统实战系列-基于ZooKeeper的分布式锁优化秒杀逻辑...
本文是"Java秒杀系统实战系列文章"的第十六篇,本文我们将继续秒杀系统的优化之路,采用统一协调调度中心中间件ZooKeeper控制秒杀系统中高并发多线程对于共享资源~代码块的并发 ...
- 【Zookeeper】基于Zookeeper实现分布式锁
1.概述 转载:基于Zookeeper实现分布式锁 1.1 为什么使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,我们往往采用synchronized或者Lock ...
- 基于ZooKeeper的分布式锁和队列
分布式锁的几种实现: 1.zookeeper分布式锁,基于自增节点 2.Redis分布式锁,基于setnx命令, 基于Redis实现分布式锁:http://blog.csdn.net/daiyudon ...
- 基于Zookeeper的分布式锁
实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...
- Zookeeper:基于Zookeeper的分布式锁与领导选举
本文转发自技术世界,原文链接 http://www.jasongj.com/zookeeper/distributedlock/ 1.Zookeeper特点 1.1 Zookeeper节点类型 如上文 ...
- 基于ZooKeeper实现分布式锁
ZooKeeper 保证了数据的强一致性, zk集群中任意节点(一个zkServer)上的相同znode下的数据一定是相同的.使用zookeeper可以非常简单的实现分布式锁, 其基本逻辑如下: 客 ...
- etcd 笔记(08)— 基于 etcd 实现分布式锁
1. 为什么需要分布式锁? 在分布式环境下,数据一致性问题一直是个难点.分布式与单机环境最大的不同在于它不是多线程而是多进程.由于多线程可以共享堆内存,因此可以简单地采取内存作为标记存储位置.而多进程 ...
- 基于 Redis 的分布式锁到底安全吗?
[完整版] 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现 ...
- redis使用sysc超时_基于redis的分布式锁实现
随着业务越来越复杂,应用服务都会朝着分布式.集群方向部署,而分布式CAP原则告诉我们,Consistency(一致性). Availability(可用性).Partition tolerance(分 ...
最新文章
- PHPExcel 出现open_basedir restriction in effect的解决办法
- 清华大学提出点云Transformer!在3D点云分类、分割上表现优秀,核心代码已开源!...
- 高铁转地铁不想重复安检?多地已有举措
- 我们为什么要把方法区分为:静态方法和实例化方法 ?(摘)
- pycharm更换源(换源)(镜像源)
- 微星笔记本电脑恢复出厂或者一键还原图文流程
- python django升级安装sqlite3后在python命令行还是显示旧版本的解决办法
- java optional 用法_Java 8中的Optional: 如何正确使用?
- mysql-gui怎么执行_MySQL 执行大量语句时的正确姿势? mysql *.sql ? 使用 GUI?
- Oracle 等待事件(Wait Event):Sync ASM rebalance 解析
- java+tomcat(apr,native)
- java类加载全过程
- excel文件工作表保护快速撤销
- 软件过程模型(详解)
- Failed to create/setup connection: 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。
- VBA IE对象的操作方法
- 百度地图图层开发java_Android百度地图应用之图层展示
- 【MySQL用法】MySQL中大于,大于等于,小于,小于等于的转义写法
- 如何快速合并PDF文件?几个方法教你合并PDF
- easyexcel复杂模板导出(合并行列,列统计汇总)