zookeeper
概述:
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper包含一个简单的原语集,提供Java和C的接口。
ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口,代码在$zookeeper_home\src\recipes。其中分布锁和队列有Java和C两个版本,选举只有Java版本。
Zookeeper端口:
zookeeper默认端口号:
- 代码访问client的端口号: 2181
- leader和flower通信的端口号: 2888
- 选举leader时通信的端口号: 3888
- 其他服务与监控中心通信端口: 7070
Zookeeper工作机制:
zookeeper从设计模式角度:是一个基于观察者模式设计的分布式管理框架,他负责管理大家都关心的数据,然后接收观察者的注册,一旦数据发生变化,zookeeper就通知已经在zookeeer上注册的观察者作出相应的反应。
zookeeper=文件系统+通知机制
zookeeper特点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZAhfH2F-1599795784135)(zookeeper.assets/image-20200907085830417.png)]
- zookeeper:一个领导者(leader),多个跟随者(follower)组成的集群。
- 集群中只要有半数以上节点存活,zookeeper集群就能正常运行
- 全局数据一致性,每个server保存一份相同的数据,client无论连接到那个server,数据都是一致的。
- 更新请求顺序进行,来自同一个client的更新请求按发送顺序以依次进行
- 数据更新原子性,一次数据更新要么成功要么失败。
- 实时性,在一定时间范围内,client能读取到最新数据
zookeeper数据结构:
Zookeeper 的数据模型图:
在 Zookeeper 中,每一个数据节点都是一个 ZNode,每个节点都默认只能存储最大1MB数据,每个Znode都可以通过其路径唯一标识上图根目录下有两个节点,分别是:app1 和 app2,其中 app1 下面又有三个子节点。那么我们来看看 ZNode 数据结构到底是什么样子的呢。首先我们来了解 ZNode 的类型。
每一个节点称为一个ZNode,每一个ZNode维持一个数据结构:
- Version number − 版本号,当和该znode节点关联的数据发生变化时,版本号会自增1。
- Action Control List (ACL) − 访问控制列表,znode的访问控制机制,它控制znode的所有读写操作。
- Timestamp − znode创建时的时间戳,精确到毫秒。
- Data length − znode节点存储数据的长度,最大1MB。
zookeeper节点类型:
Znode有两种类型:
-
短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除
-
持久(persistent):客户端和服务器端断开连接后,创建的节点不删除
Znode有四种形式的目录节点(默认是persistent ):
-
持久化目录节点(PERSISTENT)
- 客户端与zookeeper断开连接后,该节点依旧存在
- 持久节点就是节点被创建后会一直存在服务器,直到删除操作主动清除,这种节点也是最常见的类型。
-
持久化顺序编号目录节点(PERSISTENT_SEQUENTIAL)
-
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
-
持久顺序节点就是有顺序的持久节点,节点特性和持久节点是一样的,只是额外特性表现在顺序上。顺序特性实质是在创建节点的时候,会在节点名后面加上一个数字后缀,来表示其顺序。
说明:顺序号是一个单调递增的计数器,由父节点维护。
注意:在分布式系统中,顺序号可以被用于为所有事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序.
-
-
临时目录节点(EPHEMERAL)
- 客户端与zookeeper断开连接后,该节点被删除
- 临时节点就是会被自动清理掉的节点,它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久性节点不同的是,临时节点不能创建子节点。
-
临时顺序编号目录节点(EPHEMERAL_SEQUENTIAL)
- 客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
- 临时顺序节点就是有顺序的临时节点,和持久顺序节点相同,在其创建的时候会在名字后面加上数字后缀。
zookeeper应用场景:
一致性配置管理:
我们在开发的时候,有时候需要获取一些公共的配置,比如数据库连接信息等,并且偶然可能需要更新配置。如果我们的服务器有N多台的话,那修改起来会特别的麻烦,并且还需要重新启动。这里Zookeeper就可以很方便的实现类似的功能。
-
将公共的配置存放在Zookeeper的节点中
-
应用程序可以连接到Zookeeper中并对Zookeeper中配置节点进行读取或者修改(对于写操作可以进行权限验证设置)
注意:应用程序注册的watcher每次只能触发一次,当获取完消息之后需要再注册一次watcher
SpringCloud配置中心:
统一命名服务(服务注册中心):
命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等——这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表,点击这里查看Dubbo开源项目。在Dubbo实现中:
服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。
服务消费者启动的时候,订阅/dubbo/ s e r v i c e N a m e / p r o v i d e r s 目 录 下 的 提 供 者 U R L 地 址 , 并 向 / d u b b o / {serviceName}/providers目录下的提供者URL地址, 并向/dubbo/ serviceName/providers目录下的提供者URL地址,并向/dubbo/{serviceName} /consumers目录下写入自己的URL地址
注意:所有向ZK上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。
另外,Dubbo还有针对服务粒度的监控,方法是订阅/dubbo/${serviceName}目录下所有提供者和消费者的信息。
zoookepper统一集群管理:
- 分布式环境中实时掌握每个节点的状态是必要的。
- 可根据节点实时状态作出一些调整。
- zookeeper可实现实时监控节点状态的变化.
- 可将节点信息写入zookeeper上的ZNode
- 监听这个ZNode可以获取它的实时状态
zookeeper软负载均衡:
ZooKeeper会维护一个树形的数据结构,类似于Windows资源管理器目录,其中EPHEMERAL类型的节点会随着创建它的客户端断开而被删除,利用这个特性很容易实现软负载均衡。
基本原理是,每个应用的Server启动时创建一个EPHEMERAL(临时)节点,应用客户端通过读取节点列表获得可用服务器列表,并订阅节点事件,有Server宕机断开时触发事件,客户端监测到后把该Server从可用列表中删除。
- 一、获取服务器列表,通过随机数,客户端随机获取一台服务器进行访问
- 二、采用计数器方式,连接上加一,断开减一,获取计数最少的连接
zookeeper下载:
安装:
将下载安装包上传至linux,安装zookeeper需要jdk,通过java javac java -version 命令查看是否安装jdk。
java
javac
java -version
解压:
tar -zxvf zookeeper-3.4.14.tar.gz
配置:
cd zookeeper-3.4.14/conf/
cp zoo_sample.cfg zoo.cfg
vim zoo.cfg
将dataDir修改为自己定义的信息存储路径:
创建data目录:
mkdir data
启动:
#进入bin目录
cd bin
#启动zk
./zkServer.sh start
#查看进程
jsp
#查看状态
./zkServer.sh status
Mode:standalone 单击模式
退出服务:
#退出服务
./zkServer.sh stop
启动客户端:
#启动客户端
./zkCli.sh
#查看节点 注意:ls 空格 /
ls /
#退出
quit
zookeeper配置参数:
tickTime 心跳时间默认两秒,为了确保client-server连接存在的,以毫秒为单位,最小超时时间为两个心跳时间。
initLimit 多少个tickTime内,允许其他server连接并初始化数据,如果zooKeeper管理的数据较大,则应相应增大这个值。 默认10就是10*tickTime(10*2000ms == 2s)
syncLimit 多少个tickTime内,允许follower同步,如果follower落后太多,则会被丢弃。默认5 ,(5*tickTime==5*2000ms)
dataDir 用于存放内存数据库快照的文件夹,同时用于集群的myid文件也存在这个文件夹里。
dataLogDir 用于单独设置transaction log的目录,transaction log分离可以避免和普通log还有快照的竞争。
clientPort 客户端监听端口。(客户端链接服务端端口)
globalOutstandingLimit client请求队列的最大长度,防止内存溢出,默认值为1000。
preAllocSize 预分配的Transaction log空间block为proAllocSize KB,默认block为64M,一般不需要更改,除非snapshot过于频繁。
snapCount 在snapCount个snapshot后写一次transaction log,默认值是100,000。
traceFile 用于记录请求的log,打开会影响性能,用于debug,最好不要定义。
maxClientCnxns 最大并发客户端数,用于防止DOS的,默认值是10,设置为0是不加限制。
clientPortBindAddress 可以设置指定的client ip以及端口,不设置的话等于ANY:clientPort
minSessionTimeout 最小的客户端session超时时间,默认值为2个tickTime,单位是毫秒
maxSessionTimeout 最大的客户端session超时时间,默认值为20个tickTime,单位是毫秒
electionAlg
用于选举的实现的参数:
①0:为以原始的基于UDP的方式协作
②1:为不进行用户验证的基于UDP的快速选举
③2:为进行用户验证的基于UDP的快速选举
④3:为基于TCP的快速选举,默认值为3
leaderServes leader是否接受客户端连接。默认值为yes。leader负责协调更新。当更新吞吐量远高于读取吞吐量时,可以设置为不接受客户端连接,以便leader可以专注于同步协调工作。
server.x=[hostname]:nnnnn[:nnnnn]
配置集群里面的主机信息,其中:
①server.x:server.x的x要写在myid文件中,决定当前机器的id,
②第一个port用于连接leader,
③第二个用于leader选举。
④如果electionAlg为0,则不需要第二个port。
⑤hostname也可以填ip。
group.x=nnnnn[:nnnnn] 分组信息,表明哪个组有哪些节点,例如group.1=1:2:3 group.2=4:5:6 group.3=7:8:9。
weight.x=nnnnn 权重信息,表明哪个结点的权重是多少,例如weight.1=1 weight.2=1 weight.3=1。
zookeeper选举机制(重点):
- 半数机制:集群中半数以上机器存活,集群可用。所以zookeeper适合安装奇数台服务器。
- zookeeper虽然在配置文件中并没有指定Master和slaver。但是Zookeeper在工作时是有一个Leader和多个Follower,Leader是通过内部选举机制临时产生的。
3.
zookeeper提供了三种方式:
- LeaderElection
- AuthFastLeaderElection
- FastLeaderElection (最新默认)
默认的算法是FastLeaderElection,所以这篇主要分析它的选举机制。
选举流程简述:
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
- 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
- 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
- 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
- 服务器5启动,后面的逻辑同服务器4成为小弟。
选择机制中的概念:
1、Serverid:服务器ID
比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。
2、Zxid:数据ID
服务器中存放的最大数据ID.
值越大说明数据越新,在选举算法中数据越新权重越大。
3、Epoch:逻辑时钟
或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。
4、Server状态:选举状态
- LOOKING,竞选状态。
- FOLLOWING,随从状态,同步leader状态,参与投票。
- OBSERVING,观察状态,同步leader状态,不参与投票。
- LEADING,领导者状态
选举消息内容
在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
- 服务器ID
- 数据ID
- 逻辑时钟
- 选举状态
Zookeeper分布式集群安装:
方式一:
- 将第一台服务器安装配置好的zookeeper远程拷贝至多台服务器并修改拷贝后的配置
scp -r /home/administrator/test/ root@192.168.1.100:/root/
方式二:
1.在三台linux服务器上安装zookeeper
2.修改zoo.cfg:
# 配置三台的ip,广播端口(用于通讯)和选举端口
server.1=192.168.200.140:2888:3888
server.2=192.168.200.150:2888:3888
server.3=192.168.200.160:2888:3888
3.在dataDir目录下新建myid
echo 1 > myid
每个服务器创建myid时,对应zoo.cfg配置文件汇中的server.数子.如:192.168.200.140 创建:
echo 1 > myid
192.168.200.150创建时,以此类推:
echo 2 > myid
4.在三台服务器启动zookeeper:
查看zoookeeper日志:
在那个目录启动日志文件就在哪里,一般在bin目录
出现此错误则关闭linux防火墙!
关闭CentOS 7.0防火墙
CentOS 7.0默认使用的是firewall作为防火墙:
#查看防火墙状态
firewall-cmd --state
#停止firewall
systemctl stop firewalld.service
#禁止firewall开机启动
systemctl disable firewalld.service
出现java.net.ConnectException: 拒绝连接 (Connection refused):
等待其他两台服务启动后在查看状态,出现此错误因其他服务还未启动。
等待三台服务启动后查看状态:
出现一个Leader和两个Follower则成功!
zookeeper客户端命令行操作:
命令基本语法 | 功能描述 |
---|---|
help | 显示所有命令操作 |
ls path [watch] | 使用ls命令来查看当前ZNode中所包含的内容 |
ls2 path [watch] | 查看当前节点数据,并能看到更新次数等数据 |
create | 普通创建 -s 含序列 -e 临时(重启或者超时消失) |
get path [watch] | 获得节点的值 |
set | 设置节点的具体值 |
stat | 查看节点状态 |
delete | 删除节点 |
rmr | 递归删除节点 |
#查看节点
ls /
#查看节点详细信息
ls2 /
#创建节点
create /sanguo "jinlian"
#创建节点的子节点
create /sanguo/shuguo "liubei"
#获取节点数据
get /sanguo
#获取节点子节点数据
get /sanguo/shuguo
#查看节点状态
stat /sanguo
#删除节点
delete /sanguo
#递归删除
rmr /sanguo
#创建临时节点 (在退出后消失)
create -e /sanguo/wuguo "zhouyu"
#创建持久顺序节点
create -s /sanguo/weiguo "caocao"
#设置节点的值
set /sanguo/shuguo "liushan"
节点的值监听变化:
#监听三国值的变化
get /sanguo watch
在二号服务器监听三号的值变化:
在一号服务器修改三号的值:
当三国值修改后二号服务器的变化:
==注意:==watch只能使用一次,在监听到数据变化后需重新监听。
Stat结构体:
- cZxid:创建节点的事务zxid
- 每次修改Zookeeper状态都会收到一个Zxid形式的时间戳,也就是Zookeeper事务ID
- 事务ID是Zookeeper中所有修改总的次序。每个修改都有唯一的Zxid,如果Zxid1<Zxid2,那么Zxid1在Zxid2之间执行
- ctime:ZNode被创建的毫秒数(1970年开始)
- mZxid:ZNode最后更新的事务Zxid
- mtime:ZNode最后修改的毫秒数(1970年开始)
- pZxid: ZNode最后更新的子节点Zxid
- cversion:ZNode子节点变化号,ZNode子节点修改次数
- dataVersion:ZNode数据变化号
- aclVersion:ZNode访问控制列表的变化号
- ephemeralOwner:ZNode如果是临时节点,这个是ZNode拥有者的Session Id ,如果不是临时节点是0*0
- dataLength: ZNode的数据长度
- ==numChildren:==ZNode子节点数量
Zookeeper监听原理(重点):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l5snoHEX-1599795784203)(zookeeper.assets/image-20200908093358250.png)]
- 首先有一个main()线程
- 在main线程中创建Zookeeper客户端,会创建两个线程,connect负责网络连接通信,listener负责监听
- 通过connect线程将注册的监听事件发送给Zookeeper
- 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中
- Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
- listener线程内部调用了process()方法
常见的监听:
-
监听节点数据的变化:
-
get path [watch]
-
-
监听节点数量的变化
-
ls path [watch]
-
Zookeeper集群写数据流程:
JavaClientAPI:
-
创建普通maven项目:
-
添加依赖
- ==注意:==zookeeper依赖版本为3.4.6,我使用3.4.14程序有问题。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
添加log4j日志配置:
### 设置###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
常用命令API:
package com.cwh.zookeeper;
import lombok.SneakyThrows;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName : ZookeeperTest
* @Description :
* @Author : cuiweihua
* @Date: 2020-09-08 10:05
*/
public class ZookeeperTest {
/**
* 此处ip:端口也可,只写ip也可
*/
private String connectionString = "192.168.138.128:2181,192.168.138.130:2181,192.168.138.131:2181";
private Integer sessionTime = 2000;
private ZooKeeper zkClient = null;
private Logger logger = LoggerFactory.getLogger(ZookeeperTest.class);
@SneakyThrows
@Before
public void init(){
zkClient = new ZooKeeper(connectionString, sessionTime, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
logger.info("监听到节点的变化");
//重新注册监听 因为watch是一次性的
System.out.println("\n");
zkClient.getChildren("/", true).forEach(System.out::println);
System.out.println("\n");
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},false);
logger.info("zkCLient 初始化成功!");
}
/**
* 创建节点
*/
@Test
public void create(){
logger.info("开始创建节点");
String path = null;
try {
path = zkClient.create("/test2", "test".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
logger.info("节点创建完毕"+path);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 获取子节点并监控
*/
@Test
public void getZNodeAndWatch() throws KeeperException, InterruptedException {
//获取子节点不监听
/*zkClient.getChildren("/",false).forEach(System.out::println);*/
//获取子节点并监听
zkClient.getChildren("/", true);
//想要持续监听,需要使线程保持运行
while (true){
TimeUnit.SECONDS.sleep(2);
}
}
/**
* 查看节点是否存在
*/
@Test
public void exist() throws KeeperException, InterruptedException {
Stat exists = zkClient.exists("/sanguo", false);
System.out.println("节点信息:"+exists);
}
@SneakyThrows
@After
public void close(){
zkClient.close();
logger.info("zkClient 关闭连接");
}
}
实现服务器动态上下线案例:
服务端:
package com.cwh.zookeeper;
import org.apache.zookeeper.*;
import java.io.IOException;
/**
* @ClassName : Server01
* @Description :
* @Author : cuiweihua
* @Date: 2020-09-08 16:09
*/
public class Server01 {
private static final String connectionString = "192.168.138.128:2181,192.168.138.130:2181,192.168.138.131:2181";
private static final Integer sessionTime = 2000;
private static ZooKeeper zkClient = null;
public static void main(String[] args) throws KeeperException, InterruptedException {
//获取连接
getConnection();
//注册节点
register("cwh");
//业务
sleep();
}
private static void sleep() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void register(String arg) throws KeeperException, InterruptedException {
zkClient.create("/servers/server01",arg.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}
private static void getConnection() {
try {
zkClient = new ZooKeeper(connectionString, sessionTime, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package com.cwh.zookeeper;
import org.apache.zookeeper.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName : Client01
* @Description :
* @Author : cuiweihua
* @Date: 2020-09-08 16:09
*/
public class Client01 {
private static final String connectionString = "192.168.138.128:2181,192.168.138.130:2181,192.168.138.131:2181";
private static final Integer sessionTime = 2000;
private static ZooKeeper zkClient = null;
public static void main(String[] args) throws KeeperException, InterruptedException {
//获取连接
getConnection();
//客户端监听
getChlidren();
//业务
sleep();
}
private static void sleep() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void getChlidren() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren("/servers", true);
List<String> list = new ArrayList<>();
for (String child : children) {
list.add(new String(zkClient.getData("/servers/" + child, false, null)));
}
System.out.println(list);
}
private static void getConnection() {
try {
zkClient = new ZooKeeper(connectionString, sessionTime, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getChlidren();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}