基于Zookeeper使用ZkClient实现分布式锁

有段时间没写博客了,在整理之前写过的一套自定义框架,并且整理好上传值github上了,也有一些新功能还在开发,欢迎大家使用:一个好用的Http接口请求工具组件

可能今天这篇文章跟之前的比有些跳跃性,一下子就谈到了Zookeeper了,不过也没关系啦,先谈谈最常用,然后在慢慢看Zooeeper的其他知识。

简单介绍

ZooKeeper致力于提供一个高性能、高可用,且具备严格的顺序访问控制能力的分布式协调服务,是雅虎公司创建,是GoogleChubby一个开源的实现,也是HadoopHbase的重要组件。它的数据以树形结构(类似于文件系统)储存在内存当中由于数储存在内存当中(并且每个节点都必须要有数据),所以拿取数据效率特别快。

分布式锁:功能与之前并发编程中的Lock功能一致,主要是为了解决共享资源被竞争所导致的并发问题。由于并发编程当中锁是在当前的JVM当中,而对于分布式的服务来说单纯的JVM的锁已经不起作用了,不过实现功能还是一致。

监听机制

既然是锁,那么就存在线程等待以及线程被唤醒功能,所以就需要有一个监听机制,当ZooKeeper上的锁被释放之后需监听到,并且通知服务去获取锁资源,正好在ZooKeeper当中存在一种监听机制,为事件监听器(Watcher)

事件监听器:客户端可以在节点上注册监听器,当特定的事件发生后,ZooKeeper会通知到感兴趣的客户端;被监听的事件有:NodeCreated(节点创建)、NodeDeleted(节点删除)NodeDataChanged(节点数据被改变)NodeChildrenChange(子节点被修改)

Node类型

基于ZooKeeper分布式锁必然基于节点,在ZooKeeper创建节点共有四点类型:

1、持久化节点(PERSISENT): 同一个节点路径只能创建一个节点,并且连接创建节点后,断开连接后,节点仍然存在并且关闭服务会保存至磁盘上。

2、持久化顺序节点(PERSISENT_SEQUENTIAL):同一个节点路径可以创建多个节点,并且ZooKeeper会自动分配一个按顺序的节点号,断开连接后,节点仍然会保存至磁盘。

3、临时节点(EPHEMERAL):在一个连接中,同一路径下只能创建一个节点,当创建节点的连接关闭后,该节点会被删除,如果非正常关闭连接,则过一段时间后节点会被删除。

4、临时顺序节点(EPHEMERAL_SEQUENTIAL):同一路径下可以创建多个节点,但是节点名称ZooKeeper会自动分配一个按顺序的节点号,当连接关闭后,这些节点会被删除。

实现方式

先引入一下zkClient的坐标

<dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version>
</dependency>

既然是锁,那么必定是同一个节点,而且要先去尝试获取到锁,如果没有获取到,那么就进入等待,并且监听同一个节点是否被删除(或者修改),如果删除,则唤醒等待的线程,并且再次去获取ZooKeeper上的锁节点;那么整体的流程如下:

//锁节点
public static final String PATH = "/lock";
//尝试获取锁	
public abstract boolean tryLock();
//等待锁释放
public abstract void waitLock();
//释放锁
public abstract void unLock();
//获取锁
void getLock(){if(tryLock()) {//获取到锁后,进行业务操作System.out.println(Thread.currentThread().getName() + " get Lock");}else {//没有获取则进入等待,并且监听锁节点是否被释放waitLock();//再次获取锁getLock();}
}

 我这里是采用ZkClient进行操作ZooKeeper的,先创建一个ZkClient连接:

	CountDownLatch latch = null;private static final String CONNECTION = "127.0.0.1:2181";ZkClient zkClient = new ZkClient(CONNECTION,3000);

先来看看尝试获取锁可以怎样实现:

@Override
public boolean tryLock() {try {//创建临时节点,也可以创建持久化节点,到时候释放节点的时候删除就好了zkClient.createEphemeral(PATH, "1".getBytes());return true;} catch (Exception e) {//如果创建失败,则获取节点锁失败,则进入等待return false;}
}

进入等待,并且监听锁节点是否删除或者修改:

@Override
public void waitLock() {//创建监听事件IZkDataListener listen = new IZkDataListener() {public void handleDataChange(String dataPath, Object data) throws Exception {//当前方法为监听节点修改,如果节点进行修改,那么就会执行当前方法}public void handleDataDeleted(String dataPath) throws Exception {//我这里是释放锁为删除节点,删除会执行当前方法latch.countDown();}};//注册监听器zkClient.subscribeDataChanges(PATH, listen);//如果ZooKeeper上存在锁节点,那么进入等待if(zkClient.exists(PATH)) {//采用CountDownLatch等待latch = new CountDownLatch(1);try {//进入等待latch.await();} catch (InterruptedException e) {e.printStackTrace();}}//删除监听器zkClient.unsubscribeDataChanges(PATH, listen);
}

然后就是释放节点了:

@Override
public void unLock() {if(zkClient != null) {System.out.println(Thread.currentThread().getName() + " unlock.. ");//删除节点zkClient.delete(PATH);//关闭当前连接zkClient.close();}
}

这么一套流程下来,分布式锁的功能就完成了,当这种实现的功能类似JVM锁中的非公平锁,即没有先后顺序所言,如果想要达到公平锁,那么就必须得使用顺序节点进行操作了。

那么分布式公平锁监听的节点就不是同一个节点了,而是监听当前节点的上一个节点:

private String lockSeq = null;private String before = null;@Override
public boolean tryLock() {if(!zkClient.exists(PATH)) {try {zkClient.createPersistent(PATH, true);} catch (Throwable t) {//已经创建完毕,并发问题,抛异常处理}}if(lockSeq == null) {lockSeq = zkClient.createEphemeralSequential(PATH + "/", "1".getBytes());}List<String> children = zkClient.getChildren(PATH);if(lockSeq.equals(PATH + "/" + children.get(0))) {return true;}else {for(String str : children) {if(lockSeq.contains(str)) {break;}before = PATH + "/" + str;}System.out.println(Thread.currentThread().getName() + " before node:" + before);return false;}
}@Override
public void waitLock() {IZkDataListener listen = new IZkDataListener() {public void handleDataChange(String dataPath, Object data) throws Exception {}public void handleDataDeleted(String dataPath) throws Exception {latch.countDown();}};zkClient.subscribeDataChanges(before, listen);if(zkClient.exists(before)) {latch = new CountDownLatch(1);try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}}zkClient.unsubscribeDataChanges(before, listen);}@Override
public void unLock() {if(zkClient != null) {System.out.println(Thread.currentThread().getName() + " unlock.. ");System.out.println("this node:" + lockSeq + " last node:" + before);zkClient.delete(lockSeq);}
}

实现的代码如上(一个简单的实现方式,存在单应用并发问题,可以使用记录线程的方式解决并发问题),大家可以各位去试一下,个人觉得非公平锁的效率相比公平锁来说效率要高一点点,不过对应大量的分布式服务去竞争锁资源的话,个人建议还是使用公平锁,避免阻塞时间过长,导致服务业务长期停滞问题。

还会存在一个问题就是临时顺序节点在关闭服务的时候ZooKeeper上会等待几秒钟才会删除临时节点,所以建议在程序中加上Hook钩子方法进行删除。

    static {Runtime.getRuntime().addShutdownHook(new Thread(ZkClientLock::run));}private static void run() {System.out.println("关闭服务...");//删除所有临时顺序节点,避免影响其他服务zkClient.delete(lockSeq);zkClient.close();}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法题学到的一些小语言细节

1.要学会用i&#xff1b;可以简化很多代码&#xff1a;i&#xff1b;copyFromMe(i)&#xff1b;可以写成&#xff1a;copyFromeMe(i) 2.StringBuffer也跟列表一样有append函数&#xff1b; 3.if语句是分支不能进行循环&#xff0c;要写成while才能替代循环里面的判断 4. 这里的…

android 按钮带图标 阴影_android中带图标的按钮(ImageButton)怎么用

展开全部除了Android系统自带的Button按钮以外&#xff0c;还提供了带图标的按钮ImageButton要制作带图标的按钮&#xff0c;首先要在布局62616964757a686964616fe58685e5aeb931333337613163文件中定义ImageButton&#xff0c;然后通过setImageDrawable方法来设置要显示的图标。…

Zookeeper基础常用操作以及ACL权限

这次将Zookeeper的一些基础用法以及权限这块的都补充一下在这篇博客中。 上篇博客介绍了基于ZooKeeper实现的分布式锁&#xff0c;也介绍了一些ZooKeeper的节点类型以及监听机制&#xff0c;今天这里就不作过多的介绍了&#xff0c;大家也可以自行的去官方文档上看看更具体的介…

[中医经络学习一]足阳明胃经

人体有六脏&#xff08;心、肝、脾、肺、肾五脏&#xff0c;再加心包&#xff09;六腑&#xff08;胃、小肠、大肠、膀胱、胆、三焦&#xff09;&#xff0c;每个脏腑都联接着一条经络&#xff0c;一共12条经络&#xff0c;称“十二正经”&#xff0c;经络的走向在四肢两侧基本…

ThreadLocal原理解析以及是否需要调用remove方法

平常的开发过程中&#xff0c;如果有个类不是线程安全的&#xff0c;比如SimpleDateFormat&#xff0c;要使这个类在并发的过程中是线程安全的&#xff0c;那么可以将变量设置位局部变量&#xff0c;不过存在的问题就是频繁的创建对象&#xff0c;对性能和资源会有一定降低和消…

Maven基础及概念

前言 今天就来聊聊Maven的基础和一些比较概念性的东西&#xff0c;还有一些常用的Maven命令啥的&#xff0c;主要是某人脑子记不住&#xff0c;记在博客中让她自己看吧&#xff0c;省的费心给她找。 后续的文章会聊到Maven的一些比较高级用法&#xff0c;像自定义插件&#x…

织梦缩略图自动补齐绝对路径_织梦生成文章内容缩略图时自动加上域名绝对路径...

今天又接了个织梦CMS的有偿服务,客户想要后台添加文章内容的时候,缩略图自动变成带上绝对路径的格式.比如我们默认的缩略图是这样的 /uploads/allimg/150814/123P2NB-0-lp.png 他想要的效果是这样的 http://www.youwujun.com.cn/uploads/allimg/150814/123P2NB-0-lp.png大家懂我…

BUAA 436 孟竹的复习计划(二维树状数组)

题目链接&#xff1a;http://acm.buaa.edu.cn/problem/436/ 题意&#xff1a;一个数列两种操作&#xff1a;&#xff08;1&#xff09;将某个位置的数字改成另一个数字&#xff1b;&#xff08;2&#xff09;交换两个位置的数字。每次操作之后输出逆序数的个数。 思路&#xff…

Maven之pom.xml常用标签解析及镜像配置

前言 Maven仅仅是个打包工具而已&#xff0c;个人觉得没有太大必要花费在打包工具上&#xff0c;这里就列举一下个人觉得会常用标签的使用就好了&#xff0c;原理啥的基本就不太会去深度了解了&#xff0c;如果以后遇到需了解Maven工作原理的工作的话&#xff0c;到时候一定分…

idea 导入svn代码_idea导入svn项目

起初和导入git项目一样&#xff0c;file - new - project from version control - &#xff0c;这后面选 subversion。在打开的 checkout from subversion对话框中&#xff0c;输入svn地址&#xff0c;比如 svn://11.22.33.44/demo。添加一个后&#xff0c;展开新加项&#xff…

由mysql8降级到mysql5

最近在研究liferay的使用。liferay可以连接mysql数据库。电脑中装的mysql的最新版本是mysql8。于是开始按照liferay的要求进行连接。但是多番尝试后&#xff0c;均报错&#xff1a;java.sql.SQLException: java.lang.ClassCastException: java.math.BigInteger cannot be cast …

tf计算矩阵维度_tensorflow中关于 多维tensor的运算(tf.multiply, tf.matmul, tf.tensordot)...

multiply 等同与* &#xff0c;用于计算矩阵之间的element-wise 乘法&#xff0c;要求矩阵的形状必须一致(或者是其中一个维度为1)&#xff0c;否则会报错&#xff1a;import tensorflow as tfa tf.constant([1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12], shape[2, 3, 2])b tf.con…

Maven高级之插件开发

前言 终于来到了Maven的插件开发&#xff0c;其实Maven的插件并没有想象的那么难&#xff0c;刚开始讲Maven基础的时候就演示了一下JDK是如何打包的&#xff0c;Maven打包只是在JDK打包上封装了一层而已&#xff0c;Maven也支持自定义插件开发 创建 我们先使用quickstart原型…

HTTP1.1新增了五种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 、 CONNECT

200 &#xff08;成功&#xff09; 服务器已成功处理了请求。 通常&#xff0c;这表示服务器提供了请求的网页。 201 &#xff08;已创建&#xff09; 请求成功并且服务器创建了新的资源。 202 &#xff08;已接受&#xff09; 服务器已接受请求&#xff0c;但尚未处…

katalon进行app测试_Katalon API 测试 Demo

为何选择Katalon符合我们当下的情况&#xff0c;测试需要借助现有工具提高测试效率以及提高测试质量&#xff1b;为何不自己写代码&#xff1f;不是只有自己写的框架才是最好的&#xff0c;合适的才是最好的&#xff1b;katalon 支持ui、mobile、api 同时也支持脚本模式&#x…

Maven高级之archetype(原型/骨架)开发

前言 archetype这个的主要功能就是将写好的项目模块打包成一个原型&#xff0c;然后提供给其他人使用&#xff0c;这样别人就可以快速使用这个项目模板了。 这个东西虽然很多人都基本用不上&#xff0c;但原型这个东西用的好还是很方便的&#xff0c;能够在开发新项目上省去大…

深度学习在搜索业务中的探索与实践

本文根据美团高级技术专家翟艺涛在2018 QCon全球软件开发大会上的演讲内容整理而成&#xff0c;内容有修改。引言 2018年12月31日&#xff0c;美团酒店单日入住间夜突破200万&#xff0c;再次创下行业的新纪录&#xff0c;而酒店搜索在其中起到了非常重要的作用。本文会首先介绍…

cesium面积计算_cesium-长度测量和面积测量

(更新)多谢网友的提醒&#xff0c;面积测量的小问题已经修改&#xff0c;经测试可正常使用网上找的大神的实现方法有点问题&#xff0c;实现有一些bug&#xff0c;作为cesium新手一个&#xff0c;弃之不忍&#xff0c;只好硬着头皮修改了&#xff0c;不过还好问题不大&#xff…

SpringBoot自动配置原理流程

前言 新公司太忙了&#xff0c;都没啥空更新博客&#xff0c;就随便记录一下以前的学习笔记吧。SpringBoot是基于Spring上的衍生框架&#xff0c;只要看懂了Spring的话&#xff0c;学这个就比较简单了&#xff1b;SpringBoot也是在当前微服务时代下流行的框架&#xff0c;并且…

算法:对象方式数组去重

var arr [3, 1, 1, 4 , 2 , 4 , 2 , 4 , 2, 1, 1, 3, 3, 3];var ary[];var obj{};for(var i0;i<arr.length;i){var curarr[i];if(!obj[cur]){obj[cur]cur;ary.push(cur);}}console.log(ary); 复制代码