通过ZooKeeper的有序节点、节点路径不回重复、还有节点删除会触发Wathcer事件的这些特性,我们可以实现分布式锁。

一、思路

  1. zookeeper中创建一个根节点Locks,用于后续各个客户端的锁操作。
  2. 当要获取锁的时候,在Locks节点下创建“Lock_序号”的零时有序节点(临时节点为了客户端突发断开连接,则此节点消失)。
  3. 如果没有得到锁,就监控排在自己前面的序号节点,等待它的释放。
  4. 当前面的锁被释放后,触发Process方法,然后继续获取当前子节点,判断当前节点是不是第一个,是 返回锁,否 获取锁失败。

二、实现

  在实现是要了解一个类 AutoResetEvent。AutoResetEvent 常常被用来在两个线程之间进行信号发送。它有两个重要的方法:

  Set() :发送信号到等待线程以继续其工作。

  bool WaitOne():等待另一个线程发信号,只有收到信号,线程才继续往下执行 ,会一直等待下去,返回值表示是否收到信号。

  bool WaitOne(int millisecondsTimeout):等待指定时间,如果没有收到信号继续执行,返回值表示是否收到信号。

下面为具体实现方法:

public class ZooKeeperLock
{
private MyWatcher myWatcher; private string lockNode; private org.apache.zookeeper.ZooKeeper zooKeeper; public ZooKeeperLock()
{
myWatcher = new MyWatcher();
} /// <summary>
/// 获取锁
/// </summary>
/// <param name="millisecondsTimeout">等待时间</param>
/// <returns></returns>
public async Task<bool> TryLock(int millisecondsTimeout = )
{
try
{
zooKeeper = new org.apache.zookeeper.ZooKeeper("127.0.0.1", , new MyWatcher()); //创建锁节点
if (await zooKeeper.existsAsync("/Locks") == null)
await zooKeeper.createAsync("/Locks", null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //新建一个临时锁节点
lockNode = await zooKeeper.createAsync("/Locks/Lock_", null, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); //获取锁下所有节点
var lockNodes = await zooKeeper.getChildrenAsync("/Locks"); lockNodes.Children.Sort(); //判断如果创建的节点就是最小节点 返回锁
if (lockNode.Split("/").Last() == lockNodes.Children[])
return true;
else
{
//当前节点的位置
var location = lockNodes.Children.FindIndex(n => n == lockNode.Split("/").Last());
//获取当前节点 前面一个节点的路径
var frontNodePath = lockNodes.Children[location - ];
//在前面一个节点上加上Watcher ,当前面那个节点删除时,会触发Process方法
await zooKeeper.getDataAsync("/Locks/" + frontNodePath, myWatcher); //如果时间为0 一直等待下去
if (millisecondsTimeout == )
myWatcher.AutoResetEvent.WaitOne();
else //如果时间不为0 等待指定时间后,返回结果
{
var result = myWatcher.AutoResetEvent.WaitOne(millisecondsTimeout); if (result)//如果返回True,说明在指定时间内,前面的节点释放了锁(但是可能是中间节点主机宕机 导致,所以不一定触发了Process方法就是得到了锁。需要重新判断是不是第一个节点)
{
//获取锁下所有节点
lockNodes = await zooKeeper.getChildrenAsync("/Locks");
//判断如果创建的节点就是最小节点 返回锁
if (lockNode.Split("/").Last() == lockNodes.Children[])
return true;
else
return false;
}
else
return false; }
}
}
catch (KeeperException e)
{
await UnLock();
throw e;
}
return false;
} /// <summary>
/// 释放锁
/// </summary>
/// <returns></returns>
public async Task UnLock()
{
try
{
myWatcher.AutoResetEvent.Dispose();await zooKeeper.deleteAsync(lockNode);
}
catch (KeeperException e)
{
throw e;
}
} }

Process方法实现:

 public class MyWatcher : Watcher
{
public AutoResetEvent AutoResetEvent; public MyWatcher()
{
this.AutoResetEvent = new AutoResetEvent(false);
} public override Task process(WatchedEvent @event)
{
if (@event.get_Type() == EventType.NodeDeleted)
{
AutoResetEvent.Set();
}
return null;
}
}

本文源代码在:分布式实现代码

如果你认为文章写的不错,就点个【推荐】吧

服务注册中心之ZooKeeper系列(三) 实现分布式锁的更多相关文章

  1. 服务注册中心之ZooKeeper系列(一)

    一.服务注册中心介绍 分布式服务框架部署在多台不同的机器上.例如服务A是订单相关的处理服务,服务B是订单的客户的相关信息服务.此时有个需求需要在服务A中获取订单客户的信息.如下图: 此时就面临以下几个 ...

  2. 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子

    上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...

  3. 基于ZooKeeper的三种分布式锁实现

    [欢迎关注公众号:程序猿讲故事 (codestory),及时接收最新文章] 今天介绍基于ZooKeeper的分布式锁的简单实现,包括阻塞锁和非阻塞锁.同时增加了网上很少介绍的基于节点的非阻塞锁实现,主 ...

  4. Zookeeper系列3 实现分布式锁

    基本思路 1 client调用create()方法创建“/locks/_lock_”临时顺序节点,注意节点类型是EPHEMERAL_SEQUENTIAL 2 client调用getChildren(& ...

  5. [源码阅读] 阿里SOFA服务注册中心MetaServer(1)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简 ...

  6. [源码阅读] 阿里SOFA服务注册中心MetaServer(2)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(2) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(2) 0x00 摘要 0x01 MetaServer 注册 1.1 ...

  7. [源码阅读] 阿里SOFA服务注册中心MetaServer(3)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(3) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(3) 0x00 摘要 0x01 概念 1.1 分布式一致性 1.2 ...

  8. spring cloud 入门系列三:使用Eureka 搭建高可用服务注册中心

    在上一篇中分享了如何使用Eureka 进行服务治理,里面搭建的服务注册中心是单体的, 但是在实际的应用中,分布式系统为了防止单体服务宕机带来严重后果,一般都会采用服务器集群的形式,服务注册中心也是一样 ...

  9. 服务注册中心Eureka vs Zookeeper vs Consul

    前言 在现在云计算和大数据快速发展的今天,业务快速发展和变化.我们以前的单一应用难以应对这种快速的变化, 因此我们需要将以前单一的大应用不断进行差分,分成若干微小的应用或者服务,这就是微服务的思想.但 ...

随机推荐

  1. Python数据结构之单链表

    Python数据结构之单链表 单链表有后继结点,无前继结点. 以下实现: 创建单链表 打印单链表 获取单链表的长度 判断单链表是否为空 在单链表后插入数据 获取单链表指定位置的数据 获取单链表指定元素 ...

  2. 【洛谷P2584】【ZJOI2006】GameZ游戏排名系统题解

    [洛谷P2584][ZJOI2006]GameZ游戏排名系统题解 题目链接 题意: GameZ为他们最新推出的游戏开通了一个网站.世界各地的玩家都可以将自己的游戏得分上传到网站上.这样就可以看到自己在 ...

  3. spark配置参数

    ~/.bashrc export SPARK_HOME=/usr/local/spark export PATH=$PATH:$SPARK_HOME/bin /usr/local/spark/conf ...

  4. Openvswitch手册(2): OpenFlow Controller

         我们这一节主要来看Controller Controller有两种: Primary Controller: 真正控制vswitch的flow table,vswitch会保持和contro ...

  5. 【洛谷3865】 【模板】ST表(猫树)

    传送门 洛谷 Solution 实测跑的比ST表快!!! 这个东西也是\(O(1)\)的,不会可以看我上一篇Blog 代码实现 代码戳这里

  6. 2019-4-29 js学习笔记

    js学习笔记一:js数据类型   1:基本数据类型       number类型(整数,小数)      String类型          boolean类型        NaN类型其实是一个nu ...

  7. 「ZJOI2016」解题报告

    「ZJOI2016」解题报告 我大浙的省选题真是超级神仙--这套已经算是比较可做的了. 「ZJOI2016」旅行者 神仙分治题. 对于一个矩形,每次我们从最长边切开,最短边不会超过 \(\sqrt{n ...

  8. FF中flash滚轮失效的解决方案

    概述 在FF浏览器中有这样一个bug,就是当鼠标hover在flash区域的时候,滚轮会失效.原因是ff浏览器没有把滚轮事件嵌入到flash里面去.如果这个flash很小的话,比如直播的视频,会很容易 ...

  9. Kali学习笔记4:DNS信息收集

    DNS记录 A记录 A记录是用来创建到IP地址的记录. A记录设置技巧 1.如果想创建不带www的记录,即cnblog.com,在主机记录中填写@或者留空,不同的注册商可能不一样. 2.创建多个域名到 ...

  10. silverlight属性改变事件通知

    工作中遇到silverlight本身没有提供的某些属性改变事件,但又需要在属性改变时得到通知,Google搬运stack overflow,原地址 /// Listen for change of t ...