服务注册中心之ZooKeeper系列(三) 实现分布式锁
通过ZooKeeper的有序节点、节点路径不回重复、还有节点删除会触发Wathcer事件的这些特性,我们可以实现分布式锁。
一、思路
- zookeeper中创建一个根节点Locks,用于后续各个客户端的锁操作。
- 当要获取锁的时候,在Locks节点下创建“Lock_序号”的零时有序节点(临时节点为了客户端突发断开连接,则此节点消失)。
- 如果没有得到锁,就监控排在自己前面的序号节点,等待它的释放。
- 当前面的锁被释放后,触发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系列(三) 实现分布式锁的更多相关文章
- 服务注册中心之ZooKeeper系列(一)
一.服务注册中心介绍 分布式服务框架部署在多台不同的机器上.例如服务A是订单相关的处理服务,服务B是订单的客户的相关信息服务.此时有个需求需要在服务A中获取订单客户的信息.如下图: 此时就面临以下几个 ...
- 服务注册中心之ZooKeeper系列(二) 实现一个简单微服务之间调用的例子
上一篇文章简单介绍了ZooKeeper,讲了分布式中,每个微服务都会部署到多台服务器上,那服务之间的调用是怎么样的呢?如图: 1.集群A中的服务调用者如何发现集群B中的服务提供者呢? 2.集群A中的服 ...
- 基于ZooKeeper的三种分布式锁实现
[欢迎关注公众号:程序猿讲故事 (codestory),及时接收最新文章] 今天介绍基于ZooKeeper的分布式锁的简单实现,包括阻塞锁和非阻塞锁.同时增加了网上很少介绍的基于节点的非阻塞锁实现,主 ...
- Zookeeper系列3 实现分布式锁
基本思路 1 client调用create()方法创建“/locks/_lock_”临时顺序节点,注意节点类型是EPHEMERAL_SEQUENTIAL 2 client调用getChildren(& ...
- [源码阅读] 阿里SOFA服务注册中心MetaServer(1)
[源码阅读] 阿里SOFA服务注册中心MetaServer(1) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简 ...
- [源码阅读] 阿里SOFA服务注册中心MetaServer(2)
[源码阅读] 阿里SOFA服务注册中心MetaServer(2) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(2) 0x00 摘要 0x01 MetaServer 注册 1.1 ...
- [源码阅读] 阿里SOFA服务注册中心MetaServer(3)
[源码阅读] 阿里SOFA服务注册中心MetaServer(3) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(3) 0x00 摘要 0x01 概念 1.1 分布式一致性 1.2 ...
- spring cloud 入门系列三:使用Eureka 搭建高可用服务注册中心
在上一篇中分享了如何使用Eureka 进行服务治理,里面搭建的服务注册中心是单体的, 但是在实际的应用中,分布式系统为了防止单体服务宕机带来严重后果,一般都会采用服务器集群的形式,服务注册中心也是一样 ...
- 服务注册中心Eureka vs Zookeeper vs Consul
前言 在现在云计算和大数据快速发展的今天,业务快速发展和变化.我们以前的单一应用难以应对这种快速的变化, 因此我们需要将以前单一的大应用不断进行差分,分成若干微小的应用或者服务,这就是微服务的思想.但 ...
随机推荐
- select、poll、epoll
1.概念 select.poll.epoll都是事件触发机制,当等待的事件发生就触发进行处理,用于I/O复用 2.简单例子理解 3.select函数 3.1函数详解 int select(int ma ...
- 将Paul替换成Ringo
<!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...
- appium + java + WebDriverAgent实现IOS app启动
Appium v1.8.1 <dependency> <groupId>io.appium</groupId> <artifactId>ja ...
- mybatis 中使用oracle merger into
项目背景:设计到excel导入,数据量也比较大,保证性能的情况下还要考虑到:如果数据中有这条数据的主键,则更新(update),不存在的情况,执行插入(insert). mybatis代码: < ...
- 零基础Windows + JAVA(jdk)环境搭建以及eclipse安装和使用
关于java的环境搭建其实网上有很多种,下面小编就自己来操作一下吧~ java的环境搭建最主要的就是jdk的安装及环境变量设置,好了,来看看安装步骤: 一.JDK的下载与安装(java安装包) JD ...
- UE4物理动画使用
Rigid Body Body的创建. 对重要骨骼创建Body,保证Body控制的是表现和变化比较大的骨骼. 需要对Root创建Body并绑定,设置为Kinematic且不启用物理.原因是UPrimi ...
- ubuntu16 mysql在线安装
输入"sudo apt-get update"-->回车-->"输入root用户的密码"-->回车: 输入"sudo apt-get ...
- vector作为二维数组
vector本来就是可以用来代替一维数组的,vector提供了operator[]函数,可以像数组一样的操作,而且还有边界检查,动态改变大小. 这里只介绍用它来代替二维的数组,二维以上的可以依此类推. ...
- 我的C#跨平台之旅(三):使用Entity Framework开发REST API
添加NuGet引用:EntityFramework 创建实体类:Book.Author 以及数据传输类:BookDTO.BookDetailDTO,代码如下: 创建DbContext类:BookDbC ...
- appium+python自动化脚本
用pycharm,首先得把appium导入,操作如下(否则,运行程序后会报错,没有module appium) Settings->Project Interpreter,双击pip,搜索app ...