[ZooKeeper.net] 3 ZooKeeper的分布式锁

 
基于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())

有关此篇一些图片及内容借鉴了几位园友的博文,在此感谢!

http://www.cnblogs.com/Evil-Rebe/p/6057067.html

http://www.cnblogs.com/chejiangyi/p/4938400.html

转载 [ZooKeeper.net] 3 ZooKeeper的分布式锁的更多相关文章

  1. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  2. ZooKeeper典型应用场景:分布式锁

    分布式锁是控制分布式系统之间同步访问共享资源的一种方式.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要通过一些互斥手段来防止彼此之间的干扰,以保证一致 ...

  3. Zookeeper--0300--java操作Zookeeper,临时节点实现分布式锁原理

    删除Zookeeper的java客户端有  : 1,Zookeeper官方提供的原生API, 2,zkClient,在原生api上进行扩展的开源java客户端 3, 一.Zookeeper原生API ...

  4. zookeeper学习实践1-实现分布式锁

    引言 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提 ...

  5. Docker 下的Zookeeper以及.ne core 的分布式锁

    单节点 1.拉取镜像:docker pull zookeeper 2.运行容器 a.我的容器同一放在/root/docker下面,然后创建相应的目录和文件, mkdir zookeeper cd zo ...

  6. zookeeper应用场景练习(分布式锁)

    在寻常的高并发的程序中.为了保证数据的一致性.因此都会用到锁.来对当前的线程进行锁定.在单机操作中.非常好做到,比方能够採用Synchronized.Lock或者其它的读写多来锁定当前的线程.可是在分 ...

  7. ZooKeeper分布式锁的实现原理

    七张图彻底讲清楚ZooKeeper分布式锁的实现原理[石杉的架构笔记] 文章转载自:https://juejin.im/post/5c01532ef265da61362232ed#comment(写的 ...

  8. 利用Zookeeper实现分布式锁

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  9. 分布式锁实现(二):Zookeeper

    目录 前言 设计实现 一.基本算法 二.关键点 临时有序节点 监听 三.代码实现 Curator源码分析 一.基本使用 二.源码分析 后记 前言 紧跟上文的:分布式锁实现(一):Redis ,这篇我们 ...

  10. Curator实现zookeeper分布式锁的基本原理

    一.写在前面 之前写过一篇文章(<拜托,面试请不要再问我Redis分布式锁的实现原理>),给大家说了一下Redisson这个开源框架是如何实现Redis分布式锁原理的,这篇文章再给大家聊一 ...

随机推荐

  1. Visual Studio日文乱码解决方法

    ---恢复内容开始--- 验证N多网上方法之后,没能解决,最后在控制面板->时钟.语言和区域->语言中左下角点击位置,进入区域窗口,管理选项中的更改系统区域语言,设为日语,解决,截图附上. ...

  2. .net Framework 源代码 · ScrollViewer

    本文是分析 .net Framework 源代码的系列,主要告诉大家微软做 ScrollViewer 的思路,分析很简单 看完本文,可以学会如何写一个 ScrollViewer ,如何定义一个 ISc ...

  3. MySQL学习(三) SQL基础查询

    其实在数据库最经常用的当属查询操作 基本语法 SELECT [ALL | DISTINCT | DISTINCTROW ] 字段列表 AS 字段别名 [FROM 表名 WHERE 条件表示式 GROU ...

  4. Minitab系列 前言

    Matlab 在有些时候太过去强大,强大到几乎所有的数学问题都可以解决. 在这里,Matlab就像数学版的Vim,对于一般的使用者还是不想编程,想要更加的直接直观的数学处理工具,这里向大家介绍mini ...

  5. SpringCloud初体验之Eureka

    Eureka简介 SpringBoot简化了Spring工程的复杂度,之前复杂的Spring工程被拆分成了一个个小的SpringBoot工程.那么SpringBoot之间如何通讯,相互获取信息呢?这就 ...

  6. PHP7.27: MySqlhelper class

    https://github.com/ThingEngineer/PHP-MySQLi-Database-Class https://github.com/wildantea/php-pdo-mysq ...

  7. java源文件与类

    一个源文件可以包含多个类, 编译的时候,每一个类生成一个字符码文件, 源文件名可以和类名不一致,但字符码文件与类名一致, 如果类是public(公共类),源文件名必须与类名一致 命名规则:源文件的路径 ...

  8. python进程间通信--信号Signal

    信号signal 是python进程间通信多种机制中的其中一种机制.可以对操作系统进程的控制,当进程中发生某种原因而中断时,可以异步处理这个异常. 信号通过注册的方式‘挂’在一个进程中,并且不会阻塞该 ...

  9. (网页)Java日志记录框架Logback配置详解(企业级应用解决方案)(转)

    转自CSDN: 前言 Logback是现在比较流行的一个日志记录框架,它的配置比较简单学习成本相对较低,所以刚刚接触该框架的朋友不要畏惧,多花点耐心很快就能灵活应用了.本篇博文不会具体介绍Logbac ...

  10. 表单元素的required,autocomplete,list用法

    required: 当在一个表单元素如:input中加上required属性时,点击提交表单按钮,针对input会弹出一个默认的警告信息,如下图: 代码如下: <form id="my ...