前言

关于分布式锁,在互联网行业的使用场景还是比较多的,比如电商的库存扣减,秒杀活动,集群定时任务执行等需要进程互斥的场景。而实现分布式锁的手段也很多,大家比较常见的就是redis跟zookeeper,今天我们主要介绍的是基于zookeeper实现的分布式锁。

这篇文章主要借用Curator框架对zk分布式锁的实现思路,大家理解了以后完全可以自己手动实现一遍,但是在工作中还是建议使用成熟的开源框架,很多坑别人已经帮我们踩好了,除非万不得已,需要高度定制符合自己项目的需求的时候,才开始自行封装吧。

正文

zookeeper简单介绍

既然是基于zookeeper的分布式锁,首先肯定要对这个zookeeper有一定了解,这里就不过多的进行讲解,只对其跟分布式锁有关联的特性做一个简单的介绍,更多详细的功能特性大家可以参阅官方文档。

zookeeper维护着类似文件系统的数据结构,它总共有四种类型的节点

  • PERSISTENT:持久化的节点。一旦创建后,即使客户端与zk断开了连接,该节点依然存在。

  • PERSISTENT_SEQUENTIAL:持久化顺序编号节点。比PERSISTENT节点多了节点自动按照顺序编号。

  • EPHEMERAL:临时节点。当客户端与zk断开连接之后,该节点就被删除。

  • EPHEMERAL_SEQUENTIAL:临时顺序编号节点。比EPHEMERAL节点多了节点自动按照顺序编号。(分布式锁实现使用该节点类型)

Curator实现分布式锁原理

好,当我们简单了解了zk的节点类型以后,现在正式的分析Curator分布式锁的实现原理。这里我们定义了一个“/curator_lock”锁节点用来存放相关客户端创建的临时顺序节点。

假设两个客户端ClientA跟ClientB同时去争夺一个锁,此时ClientA先行一步到达zk,那么它将会在我们的zk上创建一个“/curator_lock/xxxxx-0000000000”的临时顺序节点。

接着它会获取到“/curator_lock/”锁节点下的所有子节点,因为这些节点是有序的,这时候会判断它所创建的节点是否排在第一位(也就是序号最小),由于ClientA是第一个创建节点的的客户端,必然是排在第一位,所以它也就拿到了锁。我们用zkCli查看节点信息如下。

[zk: localhost:2182(CONNECTED) 4] ls /curator_lock
[_c_f3f38067-8bff-47ef-9628-e638cfaad77e-lock-0000000000]

  

这个时候ClientB也来了,按照同样的步骤,先是在“/curator_lock/”下创建一个临时顺序节点“/curator_lock/xxxxx-0000000001”,这个节点的序号是递增的,接着也是获得该节点下的所有子节点,并比对自己当前生成的节点序号是否在这些子节点中是最小的,由于此时序号最小的节点是ClientA创建的,并且还没释放掉,所以ClientB自己就拿不到锁。

[zk: localhost:2182(CONNECTED) 4] ls /curator_lock
[_c_2a8198e4-2039-4a3c-8606-39c65790d637-lock-0000000001,
_c_f3f38067-8bff-47ef-9628-e638cfaad77e-lock-0000000000]

  

既然ClientB拿不到锁,也不会放弃,它会对自己的前一个节点加上监听器(zk提供的api实现),只要监听到前一个节点被删除了,也就是释放了锁,就会马上重新执行获取锁的操作。

当后面的ClientC,ClientD...过来的时候也是如此,变化的只是节点上的编号,它会根据Client连接的数量而不断增加。

可能大家还会担心,万一我的获取到锁的客户端宕机了怎么办,会不会不释放锁?其实上面已经解答了这个问题,由于Curator使用的是临时顺序节点来实现的分布式锁,只要客户端与zk连接断开,该节点也就消失了,相当于释放了锁。

下面代码展示了Curator的基本使用方法,仅作为参考实例,请勿在生产环境使用的这么随意。

CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2182",
5000,10000,
new ExponentialBackoffRetry(1000, 3));
client.start();
InterProcessMutex interProcessMutex = new InterProcessMutex(client, "/curator_lock");
//加锁
interProcessMutex.acquire(); //业务逻辑 //释放锁
interProcessMutex.release();
client.close();

总结

我们在搞懂了原理之后,就可以抛弃Curator,自己动手实现一个分布式锁了,相信有一定经验的工程师实现基本的功能都是没问题的,但是要做到生产级别,可能还是要在细节上下功夫,比如说一些异常处理,性能优化等因素要考虑。

该文章首发于微信公众号《深夜里的程序猿》,转载请注明出处,侵权必究。

女朋友也能看懂的Zookeeper分布式锁原理的更多相关文章

  1. zookeeper 分布式锁原理

    zookeeper 分布式锁原理: 1 大家也许都很熟悉了多个线程或者多个进程间的共享锁的实现方式了,但是在分布式场景中我们会面临多个Server之间的锁的问题,实现的复杂度比较高.利用基于googl ...

  2. zookeeper分布式锁原理

    一.分布式锁介绍分布式锁主要用于在分布式环境中保护跨进程.跨主机.跨网络的共享资源实现互斥访问,以达到保证数据的一致性. 二.架构介绍在介绍使用Zookeeper实现分布式锁之前,首先看当前的系统架构 ...

  3. zookeeper(4)--zookeeper分布式锁原理

    目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency).可用性( ...

  4. ZooKeeper 分布式锁

    在Redis分布式锁一文中, 作者介绍了如何使用Redis开发分布式锁. Redis分布式锁具有轻量高吞吐量的特点,但是一致性保证较弱.我们可以使用Zookeeper开发分布式锁,来满足对高一致性的要 ...

  5. Zookeeper基础教程(五):C#实现Zookeeper分布式锁

    分布式锁 互联网初期,我们系统一般都是单点部署,也就是在一台服务器完成系统的部署,后期随着用户量的增加,服务器的压力也越来越大,响应速度越来越慢,甚至出现服务器崩溃的情况. 为解决服务器压力太大,响应 ...

  6. ZooKeeper分布式锁简单实践

    ZooKeeper分布式锁简单实践 在分布式解决方案中,Zookeeper是一个分布式协调工具.当多个JVM客户端,同时在ZooKeeper上创建相同的一个临时节点,因为临时节点路径是保证唯一,只要谁 ...

  7. Zookeeper 分布式锁 (图解+秒懂+史上最全)

    文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...

  8. zookeeper分布式锁

    摘要:分享牛原创,zookeeper使用,zookeeper锁在实际项目开发中还是很常用的,在这里我们介绍一下zookeeper分布式锁的使用,以及我们如何zookeeper分布式锁的原理.zooke ...

  9. 关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

随机推荐

  1. Python的logging日志

    日志级别:critical > error > warning > info > debug,notset级别越高打印的日志越少,反之亦然,即debug : 打印全部的日志(n ...

  2. Robot framework(RF) Builti,Screenshot和Collections标准库介绍

    1.1  Builti标准类库 在学习一门编程语言的时候,大多教材都是从打印“hello world”开始.我们可以像编程语言一样来学习Robot Framework.虽然通过RIDE 提供“填表”一 ...

  3. 第七章 mysql 事务索引以及触发器,视图等等,很重要又难一点点的部分

    [索引] 帮助快速查询 MyISAM ,InnoDB支持btree索引 Memory 支持 btree和hash索引 存储引擎支持 每个表至少16个索引   总索引长度至少256字节   创建索引的优 ...

  4. Vue中的scoped及穿透方法

    何为scoped? 在vue文件中的style标签上,有一个特殊的属性:scoped.当一个style标签拥有scoped属性时,它的CSS样式就只能作用于当前的组件,也就是说,该样式只能适用于当前组 ...

  5. Python《学习手册:第二章-习题》

    什么是Python解释器? Python解释器是运行Python程序的程序. 什么是源代码? 源代码是为程序所写的语句:它包括文本文件(通常以.py为文件名结尾)的文件. 什么是字节码? 字节码是Py ...

  6. Python 3.6 中文手册——前言

    1. 前言 如果你在电脑上做了很多工作,最终你会发现有一些任务你想要自动化.例如,你可能希望对大量的文本文件执行搜索和替换,或者以复杂的方式重命名并排列一堆照片文件.也许你想写一个小的自定义数据库,或 ...

  7. Xor Sum 01字典树 hdu4825

    Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total S ...

  8. Reactor模式的.net版本简单实现--DEMO

    近期在学习DotNetty,遇到不少的问题.由于dotnetty是次netty的.net版本的实现.导致在网上叙述dotnetty的原理,以及实现技巧方面的东西较少,这还是十分恼人的.在此建议学习和使 ...

  9. JS奇淫巧技:防抖函数与节流函数

    应用场景 实际工作中,我们经常性的会通过监听某些事件完成对应的需求,比如: 通过监听 scroll 事件,检测滚动位置,根据滚动位置显示返回顶部按钮 通过监听 resize 事件,对某些自适应页面调整 ...

  10. redis 中如何切换db

    一台服务器上都快开启200个redis实例了,看着就崩溃了.这么做无非就是想让不同类型的数据属于不同的应用程序而彼此分开. 那么,redis有没有什么方法使不同的应用程序数据彼此分开同时又存储在相同的 ...