你还在使用复杂的 zkclient 开发 zookeeper 么?是时候用 Curator 了 !
Curator是netflix公司开源的一套zookeeper客户端,目前是Apache的顶级项目。与Zookeeper提供的原生客户端相比,Curator的抽象层次更高,简化了Zookeeper客户端的开发量。Curator解决了很多zookeeper客户端非常底层的细节开发工作,包括连接重连、反复注册watcher和NodeExistsException 异常等,对于我们日常 ZooKeeper 服务开发进行了详细的封装,例如 Leader 选举、分布式计数器、分布式锁。这就减少了技术人员在使用 ZooKeeper 时的大部分底层细节开发工作。
下面我们常用的绘画创建以及节点的管理,一起来看看如何去使用 Curator 去代替原生的 ZooKeeper 开发。
引入依赖
Curator 主要又两个关键包,curator-framework 包和 curator-recipes 包。
- curator-framework,该包是对 ZooKeeper 底层 API 的一些封装,基础功能 API 均在这个包下;
- curator-recipes,该包封装了一些 ZooKeeper 服务的高级特性,如:Cache 事件监听、选举、分布式锁、分布式 Barrier。
<dependency>
通过上面 Maven 引入依赖之后,我们就具备了使用 Curator 去操作 zookeeper 的能力。Curator 框架提供了的 API 是相当于流式的编码风格,主要是按照逻辑的先后顺序,采用调用的方式,在代码方式以及逻辑上更清晰一些。
下面,我们先来感受下这种编码风格,例如,我们要在 zookeeper 服务中创建一个 “/test/path” 的节点,然后,节点内容为 testData ,使用 Curator 框架编码如下:
client.create().forPath(“/test/path”, testData)
创建会话
通过上面,我们已经引入了 Curator 框架,并且已经知道了其编码风格,接下来,我们来看看该如何使用 Curator 去创建一个会话。
在这之前,我们先来复习下,使用zookeeper 原生客户端如何去创建会话:
try {
使用 Curator 来创建会话:
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
在定义 CuratorFramework 对象实例的时候,我们使用了 CuratorFrameworkFactory 工厂方法,下面我们来看下其关键信息:
- connectString,zookeeper 服务地址列表,如果是多个地址则用逗号分隔,如:192.168.1.1:2181,192.168.1.2:2181 ;
- retryPolicy,重试策略,当客户端发生异常退出或者与服务端失去连接的时候,可以通过设置客户端重新连接 zookeeper 服务端;
- sessionTimeoutMs,会话超时时间,作用在服务端,用来设置该条会话在 zookeeper 服务端的失效时间;
- connectionTimeoutMs,客户端连接超时时间,作用在客户端,用来限制客户端发起一个会话连接到接收 zookeeper服务端应答的时间。
现在,我们已经完成了客户端与服务端会话的建立,即证明两端具备了通信的能力下面,我们就再来看看使用 Curator 框架该如何去创建、删除以及更新节点等。
创建节点
我们知道在创建节点的时候,是需要描叙该节点是临时节点、持久节点等节点相关数据信息,使用 Curator 创建节点代码如下:
client.create().withMode(CreateMode.EPHEMERAL).forPath("path","data".getBytes());
- 通过 create 函数创建数据节点
- 通过 withMode 函数指定节点类型,是临时节点还是持久节点
- 通过 forPath 函数指定节点路径以及所存放的数据
更新节点
接下来,我们再来看看节点更新代码该如何去编写:
client.setData().forPath("path","data_update".getBytes());
- 更新节点是使用 setData 方法
- 然后通过 forPath 函数指定所需要更新的路径以及要更新的数据信息
删除节点
上面我们已经知道了如何去创建会话、创建节点以及更新节点,下面,我们再来看看使用 Curator 如何去删除节点,代码如下:
client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(10086).forPath("path");
- 删除节点是通过 delete 函数来操作的;
- guaranteed,主要是为了保障成功删除的,只要可河段会话有效,就会在后台持续发起删除请求,知道节点被成功删除;
- deletingChildrenIfNeeded,递归删除节点以及子节点;
- withVersion,指定删除节点的版本;
- forPath,指定删除节点的位置
高级特性
我们在开发 zookeeper 服务的时候,经常会遇到这种情况,如果我们注册的节点异常断开或者是遇到其他网络问题导致的连接不可用,那这个时候我们怎么能立即感应呢?
在 Curator 中是通过 ConnectionStateListener 这个监听器去实现的,它主要是用来监控会话的连接状态,当状态发生改变的时候, zookeeper 服务就会启用不同的处理方式,其会话一共有六种基本状态:
- CONNECTED,已连接,当客户端发起的会话成功连接到服务端后,该条会话的状态变为 CONNECTED 已连接状态;
- SUSPENDED,会话连接挂起,当进行 Leader 选举和 lock 锁等操作时,需要先挂起客户端的连接。注意这里的会话挂起并不等于关闭会话,也不会触发诸如删除临时节点等操作;
- RECONNECTED,重连,当已经与服务端成功连接的客户端断开后,尝试再次连接服务端后,该条会话的状态为 RECONNECTED,也就是重新连接;
- LOST,会话丢失,客户端与服务器端因为异常或超时,导致会话关闭时,该条会话的状态就变为 LOST;
- READONLY,只读,一个客户端会话调用 CuratorFrameworkFactory.Builder.canBeReadOnly() 的时候,该会话会一直处于只读模式,直到重新设置该条会话的状态类型。
接下来,我们来看看代码如何落地:
public class zkConnectionStateListener implements ConnectionStateListener {
private String zkRegPathPrefix;
private String regContent;
public zkConnectionStateListener(String zkRegPathPrefix, String regContent) {
this.zkRegPathPrefix = zkRegPathPrefix;
this.regContent = regContent;
}
@Override
public void stateChanged(CuratorFramework client, ConnectionState newState) {
if (newState == ConnectionState.LOST) {
logger.info("zk client: {} disConnected...",regContent);
while (true) {
try {
if (client.getZookeeperClient().blockUntilConnectedOrTimedOut()) {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(zkRegPathPrefix, regContent.getBytes("UTF-8"));
break;
}
} catch (InterruptedException e) {
logger.error(">> [zkConnectionStateListener]-stateChanged InterruptedException,msg= {}", e);
break;
} catch (Exception e) {
logger.error(">> [zkConnectionStateListener]-stateChanged exception,msg= {}", e);
}
}
}
}
}
在公众号菜单中可自行获取专属架构视频资料,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢我的哈
- Spark作业调度
- 如何把Photoshop改造成远程控制工具(RAT)来利用
- Office高级威胁漏洞在野利用分析
- 10行代码告诉你,为什么说Python数据可视化是一件艺术品
- 没想到你是这样的Linux | 终端下有趣的命令合集
- PhEmail:基于Python的开源网络钓鱼测试工具
- 数据库中间件mysql-proxy细节【mysql官方的中间件】
- Office CVE-2017-8570远程代码执行漏洞复现
- Java 并发包中的读写锁及其实现分析
- 深入理解 Spring 事务原理
- Chrome开发者工具的小技巧
- Java Web中JSP中6种动作概况知识点总结——每日一语法学习
- 从Flash到Silverlight进阶教程-用代码来创建动画
- 从Flash到Silverlight进阶教程-Tweener
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 基于Java图形界面的IPV4与网址的地址解析器
- 如何在千里之外能访问自己的电脑?(FRP)
- 三分钟Docker-镜像、容器实战篇
- 看懂今天这个!你就是个真正的javaer!
- 猿进化系列7——一文搞懂IO
- 猿进化系列13——一文搞懂MVC相关框架套路
- 猿进化系列16——实战之一学会SQL开发正确姿势
- 猿进化系列17——实战之一文学会前后端分离套路
- 基于Java的模拟写字板的设计与实现
- 猿思考系列2——一文搞懂同步并发套路
- 猿思考系列3——一文搞懂单例和思考的套路
- 猿思考系列3——一文学会思考的正确姿势
- 猿思考系列4——一文学会java的斗转星移动
- 猿思考系列5——一文明白java和微商那点儿事儿
- 猿思考系列8——缓存的套路也就这些