转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁
[ZooKeeper.net] 3 ZooKeeper的分布式锁
ZooKeeper 里实现分布式锁的基本逻辑:
1.zookeeper中创建一个根节点(Locks),用于后续各个客户端的锁操作。
2.想要获取锁的client都在Locks中创建一个自增序的子节点,每个client得到一个序号,如果自己的序号是最小的则获得锁。
3.如果没有得到锁,就监控排在自己前面的序号节点,并且设置默认时间,等待它的释放。
4.业务操作后释放锁,然后监控自己的节点的client就被唤醒得到锁。(例如client A需要释放锁,只需要把对应的节点1删除掉,因为client B已经关注了节点1,那么当节点1被删除后,zookeeper就会通知client B:你是序号最小的了,可以获取锁了)
释放锁的过程相对比较简单,就是删除自己创建的那个子节点即可。
解决方案目录:
Demo1 Demo2为测试场景
ZooKeepr_Lock为锁操作代码
下面贴一下代码看看

- 1 public class ZooKeeprDistributedLock : IWatcher
- 2 {
- 3 /// <summary>
- 4 /// zk链接字符串
- 5 /// </summary>
- 6 private String connectString = "127.0.0.1:2181";
- 7 private ZooKeeper zk;
- 8 private string root = "/locks"; //根
- 9 private string lockName; //竞争资源的标志
- 10 private string waitNode; //等待前一个锁
- 11 private string myZnode; //当前锁
- 12 private AutoResetEvent autoevent;
- 13 private TimeSpan sessionTimeout = TimeSpan.FromMilliseconds(50000);
- 14 private IList<Exception> exception = new List<Exception>();
- 15
- 16 /// <summary>
- 17 /// 创建分布式锁
- 18 /// </summary>
- 19 /// <param name="lockName">竞争资源标志,lockName中不能包含单词lock</param>
- 20 public ZooKeeprDistributedLock(string lockName)
- 21 {
- 22 this.lockName = lockName;
- 23 // 创建一个与服务器的连接
- 24 try
- 25 {
- 26 zk = new ZooKeeper(connectString, sessionTimeout, this);
- 27 Stopwatch sw = new Stopwatch();
- 28 sw.Start();
- 29 while (true)
- 30 {
- 31 if (zk.State == States.CONNECTING) { break; }
- 32 if (zk.State == States.CONNECTED) { break; }
- 33 }
- 34 sw.Stop();
- 35 TimeSpan ts2 = sw.Elapsed;
- 36 Console.WriteLine("zoo连接总共花费{0}ms.", ts2.TotalMilliseconds);
- 37
- 38 var stat = zk.Exists(root, false);
- 39 if (stat == null)
- 40 {
- 41 // 创建根节点
- 42 zk.Create(root, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent);
- 43 }
- 44 }
- 45 catch (KeeperException e)
- 46 {
- 47 throw e;
- 48 }
- 49 }
- 50
- 51 /// <summary>
- 52 /// zookeeper节点的监视器
- 53 /// </summary>
- 54 public virtual void Process(WatchedEvent @event)
- 55
- 56 {
- 57 if (this.autoevent != null)
- 58 {
- 59 //将事件状态设置为终止状态,允许一个或多个等待线程继续;如果该操作成功,则返回true;否则,返回false
- 60 this.autoevent.Set();
- 61 }
- 62 }
- 63
- 64 public virtual bool tryLock()
- 65 {
- 66 try
- 67 {
- 68 string splitStr = "_lock_";
- 69 if (lockName.Contains(splitStr))
- 70 {
- 71 //throw new LockException("lockName can not contains \\u000B");
- 72 }
- 73 //创建临时子节点
- 74 myZnode = zk.Create(root + "/" + lockName + splitStr, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.EphemeralSequential);
- 75 Console.WriteLine(myZnode + " 创建完成! ");
- 76 //取出所有子节点
- 77 IList<string> subNodes = zk.GetChildren(root, false).ToList<string>();
- 78 //取出所有lockName的锁
- 79 IList<string> lockObjNodes = new List<string>();
- 80 foreach (string node in subNodes)
- 81 {
- 82 if (node.StartsWith(lockName))
- 83 {
- 84 lockObjNodes.Add(node);
- 85 }
- 86 }
- 87 Array alockObjNodes = lockObjNodes.ToArray();
- 88 Array.Sort(alockObjNodes);
- 89 Console.WriteLine(myZnode + "==" + lockObjNodes[0]);
- 90 if (myZnode.Equals(root + "/" + lockObjNodes[0]))
- 91 {
- 92 //如果是最小的节点,则表示取得锁
- 93 Console.WriteLine(myZnode + " 获取锁成功! ");
- 94 return true;
- 95 }
- 96 //如果不是最小的节点,找到比自己小1的节点
- 97 string subMyZnode = myZnode.Substring(myZnode.LastIndexOf("/", StringComparison.Ordinal) + 1);
- 98 waitNode = lockObjNodes[Array.BinarySearch(alockObjNodes, subMyZnode) - 1];
- 99 }
- 100 catch (KeeperException e)
- 101 {
- 102 throw e;
- 103 }
- 104 return false;
- 105 }
- 106
- 107
- 108 public virtual bool tryLock(TimeSpan time)
- 109 {
- 110 try
- 111 {
- 112 if (this.tryLock())
- 113 {
- 114 return true;
- 115 }
- 116 return waitForLock(waitNode, time);
- 117 }
- 118 catch (KeeperException e)
- 119 {
- 120 throw e;
- 121 }
- 122 }
- 123
- 124 /// <summary>
- 125 /// 等待锁
- 126 /// </summary>
- 127 /// <param name="lower">需等待的锁节点</param>
- 128 /// <param name="waitTime">等待时间</param>
- 129 /// <returns></returns>
- 130 private bool waitForLock(string lower, TimeSpan waitTime)
- 131 {
- 132 var stat = zk.Exists(root + "/" + lower, true);
- 133 //判断比自己小一个数的节点是否存在,如果不存在则无需等待锁,同时注册监听
- 134 if (stat != null)
- 135 {
- 136 Console.WriteLine("Thread " + System.Threading.Thread.CurrentThread.Name + " waiting for " + root + "/" + lower);
- 137 autoevent = new AutoResetEvent(false);
- 138 //阻止当前线程,直到当前实例收到信号,使用 TimeSpan 度量时间间隔并指定是否在等待之前退出同步域
- 139 bool r = autoevent.WaitOne(waitTime);
- 140 autoevent.Dispose();
- 141 autoevent = null;
- 142 return r;
- 143 }
- 144 else return true;
- 145 }
- 146
- 147 /// <summary>
- 148 /// 解除锁
- 149 /// </summary>
- 150 public virtual void unlock()
- 151 {
- 152 try
- 153 {
- 154 Console.WriteLine("unlock " + myZnode);
- 155 zk.Delete(myZnode, -1);
- 156 myZnode = null;
- 157 zk.Dispose();
- 158 }
- 159 catch (KeeperException e)
- 160 {
- 161 throw e;
- 162 }
- 163 }
- 164 }

然后先看demo2 : 当前获取到锁以后 释放锁的操作被阻塞 然后运行demo1 进行测试

- int count = 1;//库存 商品编号1079233
- if (count == 1)
- {
- ZooKeeprDistributedLock zklock = new ZooKeeprDistributedLock("Getorder_Pid1079233");
- //创建锁
- if (zklock.tryLock(TimeSpan.FromMilliseconds(50000)))
- {
- Console.WriteLine("Demo2创建订单成功!");
- }
- else { Console.WriteLine("Demo2创建订单失败了!"); }
- Thread.Sleep(30000);//对操作释放锁进行阻塞
- Console.WriteLine(DateTime.Now.ToString("yyyyMMdd HH:mm:ss")); //要进行释放锁的操作时间 主要测试当前锁释放后 Demo1的节点监控是否唤起
- zklock.unlock();//释放锁
- Console.ReadKey();
- }

demo1:demo1会对排在前面的节点进行监控 当demo2释放锁后 demo1获取锁 demo1创建订单与释放锁之间打印了操作时间
可以跟demo2进行释放锁的时间进行对比下

- int count = 1;//库存 商品编号1079233
- if (count == 1)
- {
- ZooKeeprDistributedLock zklock = new ZooKeeprDistributedLock("Getorder_Pid1079233");
- if (zklock.tryLock(TimeSpan.FromMilliseconds(50000)))
- {
- Console.WriteLine("Demo1创建订单成功!");
- }
- else
- {
- Console.WriteLine("Demo1创建订单失败了!");
- }
- Console.WriteLine(DateTime.Now.ToString("yyyyMMdd HH:mm:ss"));
- zklock.unlock();
- Console.ReadKey();
- }

这里是我运行后的结果,只精确到秒,可以看到demo2释放锁后,demo1的AutoResetEvent立即被阻断了然后demo1也就获得了锁!
场景二:将demo1的监听注释后,demo2未释放锁,demo1创建订单失败
- //if (zklock.tryLock(TimeSpan.FromMilliseconds(50000)))
- if (zklock.tryLock())
有关此篇一些图片及内容借鉴了几位园友的博文,在此感谢!
转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁的更多相关文章
- ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...
- ZooKeeper典型应用场景:分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致 ...
- Zookeeper--0300--java操作Zookeeper,临时节点实现分布式锁原理
删除Zookeeper的java客户端有 : 1,Zookeeper官方提供的原生API, 2,zkClient,在原生api上进行扩展的开源java客户端 3, 一.Zookeeper原生API ...
- zookeeper学习实践1-实现分布式锁
引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...
- Docker 下的Zookeeper以及.ne core 的分布式锁
单节点 1.拉取镜像:docker pull zookeeper 2.运行容器 a.我的容器同一放在/root/docker下面,然后创建相应的目录和文件, mkdir zookeeper cd zo ...
- zookeeper应用场景练习(分布式锁)
在寻常的高并发的程序中.为了保证数据的一致性.因此都会用到锁.来对当前的线程进行锁定.在单机操作中.非常好做到,比方能够採用Synchronized.Lock或者其它的读写多来锁定当前的线程.可是在分 ...
- ZooKeeper分布式锁的实现原理
七张图彻底讲清楚ZooKeeper分布式锁的实现原理[石杉的架构笔记] 文章转载自:https://juejin.im/post/5c01532ef265da61362232ed#comment(写的 ...
- 利用Zookeeper实现分布式锁
特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...
- 分布式锁实现(二):Zookeeper
目录 前言 设计实现 一.基本算法 二.关键点 临时有序节点 监听 三.代码实现 Curator源码分析 一.基本使用 二.源码分析 后记 前言 紧跟上文的:分布式锁实现(一):Redis ,这篇我们 ...
- Curator实现zookeeper分布式锁的基本原理
一.写在前面 之前写过一篇文章(<拜托,面试请不要再问我Redis分布式锁的实现原理>),给大家说了一下Redisson这个开源框架是如何实现Redis分布式锁原理的,这篇文章再给大家聊一 ...
随机推荐
- 如何为你的树莓派安装一个WIN10系统?(非iot)
Windows10 ARM版,是的,这次并非IoT版,而是功能与PC一致的ARM版.需要注意的是,这个方法并非官方提供的,可用性上会有一些坑,热衷于尝试的玩家可以一试! 准备项目:树莓派3B以上型号, ...
- 【测试记录】EF插入查询性能
介绍 背景什么就不提了,无外乎出现了大数据需要处理.简单的说就是我测试了EF正常的插入以及一个优化小方式而已,然后做了查询记录.其余没有什么,写这篇只是为了记录结果方便以后数据参考吧. 代码介 ...
- PPT文件流转为图片,并压缩成ZIP文件输出到指定目录
实现流程: 接收InputStream流->复制流->InputStream流转为PPT->PPT转为图片->所有图片压缩到一个压缩文件下 注意: 1.PPT文件分为2003和 ...
- 01-初始Java
1. 你学习编程的目的是什么?学习编程最快的办法是什么? 答:我喜欢计算机,想更多的了解计算机的原理:我认为学习最快的办法就是尝试,只有不断地在计算机上尝试编程,遇到错误, 解决错误,才能更快的学会编 ...
- elasticsearch安装之各种坑
我用的是centos6.5,安装elasticsearch5.2.0 首先不说了,安装JDK1.8,下载elasticsearch5.2.0 https://www.elastic.co/downlo ...
- array.js
// “最后加” concat 连接两个或更多的数组,并返回结果. var a = ['a','b','c']; var b = ['x','y','z']; var c = a.concat(b,t ...
- 《Attention is All You Need》浅读(简介+代码)
2017年中,有两篇类似同时也是笔者非常欣赏的论文,分别是FaceBook的<Convolutional Sequence to Sequence Learning>和Google的< ...
- 【转】MySQL:日期函数、时间函数总结(MySQL 5.X)
转自:http://www.cnblogs.com/she27/articles/1377089.html 一.MySQL 获得当前日期时间 函数1.1 获得当前日期+时间(date + time)函 ...
- [20180627]测试bbed是否支持管道命令.txt
[20180627]测试bbed是否支持管道命令.txt --//测试bbed是否支持管道命令.txt 1.环境:SCOTT@test01p> @ ver1PORT_STRING ...
- python第三十六天-----类中的特殊成员方法
__doc__ 查看尖的描述信息 __module__表示当前操作的对象所在的模块 __class__表示当前操作的对象所属的类 __init__构造方法 通过类创建对象自动执行 __del__析构方 ...