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 节点获取锁,删除节点释放锁。详细的过程如下:

  1. 多个客户端竞争创建 lock 临时节点
  2. 其中某个客户端成功创建 lock 节点,其他客户端对 lock 节点设置 watcher
  3. 持有锁的客户端删除 lock 节点或该客户端崩溃,由 Zookeeper 删除 lock 节点
  4. 其他客户端获得 lock 节点被删除的通知
  5. 重复上述4个步骤,直至无客户端在等待获取锁了

上面即独占锁具体的实现步骤,理解起来并不复杂,这里不再赘述。

图2 获取独占锁流程图

2.2 读写锁的实现

说完独占锁的实现,这节来说说读写锁的实现。读写锁包含一个读锁和写锁,操作 O1 对资源 R1 加读锁,且获得了锁,其他操作可同时对资源 R1 设置读锁,进行共享读操作。如果操作 O1 对资源 R1 加写锁,且获得了锁,其他操作再对资源 R1 设置不同类型的锁都会被阻塞。总结来说,读锁具有共享性,而写锁具有排他性。那么在 Zookeeper 中,我们可以用怎样的节点结构实现上面的操作呢?

图3 读写锁的 Zookeeper 节点结构

在 Zookeeper 中,由于读写锁和独占锁的节点结构不同,读写锁的客户端不用再去竞争创建 lock 节点。所以在一开始,所有的客户端都会创建自己的锁节点。如果不出意外,所有的锁节点都能被创建成功,此时锁节点结构如图3所示。之后,客户端从 Zookeeper 端获取 /share_lock 下所有的子节点,并判断自己能否获取锁。如果客户端创建的是读锁节点,获取锁的条件(满足其中一个即可)如下:

  1. 自己创建的节点序号排在所有其他子节点前面
  2. 自己创建的节点前面无写锁节点

如果客户端创建的是写锁节点,由于写锁具有排他性。所以获取锁的条件要简单一些,只需确定自己创建的锁节点是否排在其他子节点前面即可。

不同于独占锁,读写锁的实现稍微复杂一下。读写锁有两种实现方式,各有异同,接下来就来说说这两种实现方式。

读写锁的第一种实现

第一种实现是对 /share_lock 节点设置 watcher,当 /share_lock 下的子节点被删除时,未获取锁的客户端收到 /share_lock 子节点变动的通知。在收到通知后,客户端重新判断自己创建的子节点是否可以获取锁,如果失败,再次等待通知。详细流程如下:

  1. 所有客户端创建自己的锁节点
  2. 从 Zookeeper 端获取 /share_lock 下所有的子节点,并对 /share_lock 节点设置 watcher
  3. 判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则继续等待
  4. 持有锁的客户端删除自己的锁节点,其他客户端收到 /share_lock 子节点变动的通知
  5. 重复步骤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 即可。详细的流程如下:

  1. 所有客户端创建自己的锁节点
  2. 从 Zookeeper 端获取 /share_lock 下所有的子节点
  3. 判断自己创建的锁节点是否可以获取锁,如果可以,持有锁。否则对自己关心的锁节点设置 watcher
  4. 持有锁的客户端删除自己的锁节点,某个客户端收到该节点被删除的通知,并获取锁
  5. 重复步骤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 的分布式锁实现相关推荐

  1. ieee39节点系统介绍_Java秒杀系统实战系列-基于ZooKeeper的分布式锁优化秒杀逻辑...

    本文是"Java秒杀系统实战系列文章"的第十六篇,本文我们将继续秒杀系统的优化之路,采用统一协调调度中心中间件ZooKeeper控制秒杀系统中高并发多线程对于共享资源~代码块的并发 ...

  2. 【Zookeeper】基于Zookeeper实现分布式锁

    1.概述 转载:基于Zookeeper实现分布式锁 1.1 为什么使用分布式锁 我们在开发应用的时候,如果需要对某一个共享变量进行多线程同步访问的时候,我们往往采用synchronized或者Lock ...

  3. 基于ZooKeeper的分布式锁和队列

    分布式锁的几种实现: 1.zookeeper分布式锁,基于自增节点 2.Redis分布式锁,基于setnx命令, 基于Redis实现分布式锁:http://blog.csdn.net/daiyudon ...

  4. 基于Zookeeper的分布式锁

    实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,其中前两种方案网络上有很多资料可以参考,本文不做展开.我们来看下使用Zookeeper如何实现分布式锁. 什么是 ...

  5. Zookeeper:基于Zookeeper的分布式锁与领导选举

    本文转发自技术世界,原文链接 http://www.jasongj.com/zookeeper/distributedlock/ 1.Zookeeper特点 1.1 Zookeeper节点类型 如上文 ...

  6. 基于ZooKeeper实现分布式锁

    ZooKeeper 保证了数据的强一致性,  zk集群中任意节点(一个zkServer)上的相同znode下的数据一定是相同的.使用zookeeper可以非常简单的实现分布式锁, 其基本逻辑如下: 客 ...

  7. etcd 笔记(08)— 基于 etcd 实现分布式锁

    1. 为什么需要分布式锁? 在分布式环境下,数据一致性问题一直是个难点.分布式与单机环境最大的不同在于它不是多线程而是多进程.由于多线程可以共享堆内存,因此可以简单地采取内存作为标记存储位置.而多进程 ...

  8. 基于 Redis 的分布式锁到底安全吗?

    [完整版] 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现 ...

  9. redis使用sysc超时_基于redis的分布式锁实现

    随着业务越来越复杂,应用服务都会朝着分布式.集群方向部署,而分布式CAP原则告诉我们,Consistency(一致性). Availability(可用性).Partition tolerance(分 ...

最新文章

  1. PHPExcel 出现open_basedir restriction in effect的解决办法
  2. 清华大学提出点云Transformer!在3D点云分类、分割上表现优秀,核心代码已开源!...
  3. 高铁转地铁不想重复安检?多地已有举措
  4. 我们为什么要把方法区分为:静态方法和实例化方法 ?(摘)
  5. pycharm更换源(换源)(镜像源)
  6. 微星笔记本电脑恢复出厂或者一键还原图文流程
  7. python django升级安装sqlite3后在python命令行还是显示旧版本的解决办法
  8. java optional 用法_Java 8中的Optional: 如何正确使用?
  9. mysql-gui怎么执行_MySQL 执行大量语句时的正确姿势? mysql *.sql ? 使用 GUI?
  10. Oracle 等待事件(Wait Event):Sync ASM rebalance 解析
  11. java+tomcat(apr,native)
  12. java类加载全过程
  13. excel文件工作表保护快速撤销
  14. 软件过程模型(详解)
  15. Failed to create/setup connection: 驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。
  16. VBA IE对象的操作方法
  17. 百度地图图层开发java_Android百度地图应用之图层展示
  18. 【MySQL用法】MySQL中大于,大于等于,小于,小于等于的转义写法
  19. 如何快速合并PDF文件?几个方法教你合并PDF
  20. easyexcel复杂模板导出(合并行列,列统计汇总)

热门文章

  1. 1号店11.11:从应用架构落地点谈高可用高并发高性能--转载
  2. facade 模式和gateway模式的区别
  3. JPDA 利用Eclipse和Tomcat进行远程调试 --转
  4. 【Python】序列通用操作
  5. 揭秘Sponge:统一Hadoop、Spark、SDS、Swift的大数据操作系统
  6. DMLC深盟分布式深度机器学习开源平台解析
  7. 小工匠聊架构-Redis 缓存一致性设计
  8. LAN交换机自学习算法
  9. PHP从远程mysql下载文件_PHP下载远程文件到指定目录
  10. 学习笔记Hive(一)—— Hive简介