Day 07-zookeeper

1 zookeeper简介

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件(HA搭建)。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:分布式配置同步、域名服务(统一域名 负载均衡)、分布式同步、组服务(集群管理)等。
ZooKeeper是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户
zookeeper提供的功能
1 记录数据 [记录一些重要的数据] 小 快 安全
2 读取数据
3 监听通知
具体我们可以做:
• 实现HA配置 , 服务器上的上下线感知
•分布式配置文件同步
•统一域名
•分布式锁的实现 … …

简单
Zookeeper的核心是一个精简的文件系统,它支持一些简单的操作和一些抽象操作,例如,排序和通知。
丰富
Zookeeper的原语操作是很丰富的,可实现一些协调数据结构和协议。例如,分布式队列、分布式锁和一组同级别节点中的“领导者选举”。
高可靠
Zookeeper支持集群模式,可以很容易的解决单点故障问题。数据安全可靠
松耦合交互
不同进程间的交互不需要了解彼此,甚至可以不必同时存在,某进程在zookeeper中留下消息后,该进程结束后其它进程还可以读这条消息。
资源库
Zookeeper实现了一个关于通用协调模式的开源共享存储库,能使开发者免于编写这类通用协议。

2 zookeeper基础知识

2.1 安装

zookeeper集群的搭建 ,准备奇数台机器
下载
#wget http://mirror.bit.edu.cn/apache/zookeeper/stable/zookeeper-3.4.12.tar.gz
解压
tar -zxvf
重命名 zoo_sample.cfg文件
Bash
cp conf/zoo_sample.cfg conf/zoo.cfg

修改配置文件
在[root@linux01 zookeeper-3.4.6]# mkdir zkData
#vim conf/zoo.cfg

Bash
dataDir = /opt/apps/zookeeper-3.4.6/zkData
clientPort=2181
server.1=linux01:2888:3888
server.2=linux02:2888:3888
server.3=linux03:2888:3888

在/opt/apps/zookeeper-3.4.6/zkData下 创建一个配置文件 myid
Vi myid
1

分发安装包
Bash
scp -r zookeeper-3.4.6/ linux02: P W D s c p − r z o o k e e p e r − 3.4.6 / l i n u x 03 : PWD scp -r zookeeper-3.4.6/ linux03: PWDscprzookeeper3.4.6/linux03:PWD
修改每个节点的id号
Bash
[root@linux02 ~]# vi /opt/apps/zookeeper-3.4.6/zkData/myid 2
[root@linux03 ~]# vi /opt/apps/zookeeper-3.4.6/zkData/myid 3
配置文件说明
•tickTime:这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。

•initLimit:这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。当已经超过 10个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 10*2000=20 秒

•syncLimit:这个配置项标识 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是 5*2000=10秒

•dataDir:顾名思义就是 Zookeeper 保存数据的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里。

•clientPort:这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。

•server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口;D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。

启动zk实例
在每台zookeeper节点上执行启动命令 可以是绝对路径 可以是相对路径 指定配置文件
./bin/zkServer.sh start ./conf/zoo.cfg – 启动单节点
./bin/zkServer.sh stop ./conf/zoo.cfg – 停止单节点
./bin/zkServer.sh status – 在每个节点上查看节点状态
JMX enabled by default
Using config: ./conf/zoo.cfg
linux01 --> Mode: follower
linux02 --> Mode: leader
linux03 --> Mode: follower
编写一键启停脚本
Bash
#!/bin/bash

for hostname in linux01 linux02 linux03
do
echo “---------------------$hostname $1 -------------------------”
ssh $hostname "source /etc/profile ; /opt/apps/zookeeper-3.4.6/bin/zkServer.sh $1 /opt/apps/zookeeper-3.4.6/conf/zoo.cfg "

done

2.2 zookeeper角色

zkServer.sh status
Leader
Leader服务器在整个正常运行期间有且仅有一台,集群会通过选举的方式选举出Leader服务器,由它同统一处理集群的事务性请求以及集群内各服务器的调度。
Follower
Follower的主要职责有以下几点:
1.参与Leader选举投票
2.参与事务请求Proposal的投票
3.处理客户端非事务请求(读),并转发事务请求(写)给Leader服务器。
Observer
Observer是弱化版的Follower。其像Follower一样能够处理非事务也就是读请求,并转发事务请求给Leader服务器,但是其不参与任何形式的投票,不管是Leader选举投票还是事务请求Proposal的投票。引入这个角色主要是为了在不影响集群事务处理能力的前提下提升集群的非事务处理的吞吐量。

2.3 选举机制

每个zk节点都有一个myid 1 2 3
•初始选举
1 启动linux01 [1] , 发现集群中没有leader ,进入到选举状态 , 给自己投一票 (linux01 id=1 1)
2 linux01 广播自己的投票信息 linux02 linux03
3 启动linux02 [2] , 发现集群中没有leader , 进入到选举状态 , 收到linux01的广播投票信息 , 发现自己的 id=2 2>1 ; 投票 投自己一票 ; (linux02 id=2 1)
4 linux02 广播自己的投票信息 linux01 linux03
5 linux01 获取到linux02的广播信息, 发现 自己的id =1 1 <2 ; 改投linux02 一票 , 广播
6 (linux02 id=2 1+1) , linux02机器的票数过半 , 切换成leader状态 , 广播
7 linux01 直接切换到follower状态
8 linux03 启动 发现集群中有leader , 切换到follower状态
•运行容错时选举 数据同步问题

id=1的服务器启动了,它向局域网发送组播寻找leader(端口2888)。发现没有leader,这台服务器就进入选举状态(3888端口),并向局域网组播投票(投自己);
id=2的服务器启动了,它向局域网寻找leader,发现没有,进入投票状态(3888端口),收到服务器1所投的票;然后发现1<自己2,投票(投2);此时,服务1也会收到2的投票,发现2>自己,重新投(投2);此时,服务器2会收到两票;然后通过配置文件获知集群总共有3台机器,从而知道自己已经得多数票(当选);服务器2就切换到leader状态(2888);
id=3的服务器启动了,它向局域网寻找leader,发现服务器2是leader,自己主动进入follower状态;

2.4 zookeeper的数据模型

Zookeeper将数据存储于内存中,具体而言,Znode是存储数据的最小单元。而Znode被以层次化的结构进行组织,形容一棵树。其对外提供的视图类似于Unix文件系统。树的根Znode节点相当于Unix文件系统的根路径。正如Unix中目录下可以有子目录一样,Znode结点下也可以挂载子结点,最终形成如下所示结构。 znode

以文件系统进行类比的话,Znode天然具有目录和文件两重属性:即Znode既可以当做文件往里面写东西,又可以当做目录在下面挂载其他Znode。当然,由于Znode具有不同的类型,后半部分并不完全准确。
ZK的数据节点类型
Znode按其生命周期的长短可以分为持久结点(PERSISTENT)和临时结点(EPHEMERAL)
1.持久结点(PERSISTENT) 默认
最常见的Znode类型,一旦创建将在一直存在于服务端,除非客户端通过删除操作进行删除。持久结点下可以创建子结点。

2.持久顺序结点(PERSISTENT_SEQUENTIAL) -s
在具有持久结点基本特性的基础上,会通过在结点路径后缀一串序号来区分多个子结点创建的先后顺序。这工作由Zookeeper服务端自动给我们做,只要在创建Znode时指定结点类型为该类型。

3.临时结点(EPHEMERAL) -e
临时结点的生命周期和客户端会话保持一致。客户端段会话存在的话临时结点也存在,客户端会话断开则临时结点会自动被服务端删除。临时结点下不能创建子结点。

  1. 临时顺序结点(EPHEMERAL_SEQUENTIAL) -e -s
    具有临时结点的基本特性,又有顺序性。
    ZK数据的一致性
    Zookeeper采用ZAB(Zookeeper Atomic Broadcast)协议来保证分布式数据一致性
    ZAB并不是一种通用的分布式一致性算法,而是一种专为Zookeeper设计的崩溃可恢复的原子消息广播算法。ZAB协议包括两种基本模式:崩溃恢复模式和消息广播模式。崩溃恢复模式主要用来在集群启动过程,或者Leader服务器崩溃退出后进行新的Leader服务器的选举以及数据同步;消息广播模式主要用来进行事务请求的处理
    事务请求的处理流程
    ZAB协议的核心是定义了对事务请求的处理方式,整个过程可以概括如下:
    1.所有的事务请求都交由集群的Leader服务器来处理,Leader服务器会将一个事务请求转换成一个Proposal(提议),并为其生成一个全局递增的唯一ID,这个ID就是事务ID,即ZXID,Leader服务器对Proposal是按其ZXID的先后顺序来进行排序和处理的。
    2.之后Leader服务器会将Proposal放入每个Follower对应的队列中(Leader会为每个Follower分配一个单独的队列),并以FIFO的方式发送给Follower服务器。
    3.Follower服务器接收到事务Proposal后,首先以事务日志的方式写入本地磁盘,并且在成功后返回Leader服务器一个ACK响应。
    4.Leader服务器只要收到过半Follower的ACK响应,就会广播一个Commit消息给Follower以通知其进行Proposal的提交,同时Leader自身也会完成Proposal的提交。
    整个过程如下图所示

2.5 ZK事件监听机制

Zookeeper中可以通过Watcher来实现事件监听机制。客户端可以向服务端注册Watcher用以监听某些事件,一旦该事件发生,服务端即会向客户端发送一个通知。其主要工作流程如下图所示

事件监听特征
1.当监听器监听的事件被触发,服务端会发送通知给客户端,但通知信息中不包括事件的具体内容。以监听ZNode结点数据变化为例,当Znode的数据被改变,客户端会收到事件类型为NodeDataChanged的通知,但该Znode的数据改变成了什么客户端无法从通知中获取,需要客户端在收到通知后手动去获取。
2.Watcher是一次性的。一旦被触发将会失效。如果需要反复进行监听就需要反复进行注册。这么设计是为了减轻服务端的压力,但是对开发者而言却是相当不友好,不过不用着急,可以通过一些Zookeeper的开源客户端轻松实现对某一事件的永久监听。
ZK保证服务故障的容错
Zookeeper通过事务日志和数据快照来避免因为服务器故障导致的数据丢失。
事务日志是指服务器在更新内存数据前先将事务操作以日志的方式写入磁盘,Leader和Follower服务器都会记录事务日志。
数据快照是指周期性通过深度遍历的方式将内存中的树形结构数据转入外存快照中。但要注意这种快照是"模糊"的,因为可能在做快照时内存数据发生了变化。但是因为Zookeeper本身对事务操作进行了幂等性保证,故在将快照加载进内存后会通过执行事务日志的方式来讲数据恢复到最新状态。

3 ZK客户端

ZK提供了多种客户端操作的方式
zookeeper安装包中自带的命令行客户端
zookeeper安装包的java库(java客户端)

3.1 自带命令行

shell连接到ZK的任意一台节点上
bin/zkCli.sh -server linux01:2181 指定服务器

bin/zkCli.sh 连接本地服务器
Bash
[zk: linux02:2181(CONNECTED) 1] 客户端交互窗口

[zk: linux02:2181(CONNECTED) 1] help – 帮助命令 查看说有的命令
ZooKeeper -server host:port cmd args
stat path [watch]
set path data [version]
ls path [watch]
delquota [-n|-b] path
ls2 path [watch]
setAcl path acl
setquota -n|-b val path
history
redo cmdno
printwatches on|off
delete path [version]
sync path
listquota path
rmr path
get path [watch]
create [-s] [-e] path data acl
addauth scheme auth
quit
getAcl path
close
connect host:port

回补:
•Zookeeper记录数据 ,提供读取操作
•zookeeper中的数据组织结构 类似于文件系统的目录结构 (树)
•因为zookeeper中记录的数据经常发生数据内容的变化和子节点个数变化
•zookeeper中的数据就是 node节点
Bash
– 1 写数据 创建一个节点
[zk: linux02:2181(CONNECTED) 1] create /doit doit36
[zk: linux02:2181(CONNECTED) 1] create /doe doe
注意: create [-s] [-e] path data acl path 必须以 / 开始
-s 维护一个自增的序号 避免节点名重复而创建失败
[zk: linux02:2181(CONNECTED) 16] create -s /datanode linux02
Created /datanode0000000003
[zk: linux02:2181(CONNECTED) 17] create -s /datanode linux02
Created /datanode0000000004
-e 临时节点 只对当前会话有效的节点
create -e /tmp aaaaa 临时节点 /tmp 当连接会话断开以后 这个节点会自动删除
-s -e 有序的临时节点
acl 进行权限控制

– 2 读取数据
[zk: linux02:2181(CONNECTED) 8] get /doit
doit36
cZxid = 0x200000004
注意 : get path [watch] path 必须以 / 开始
– 3 ls 或者是 ls2 查看所有的节点 ls2 显示详细信息
[zk: linux02:2181(CONNECTED) 12] ls2 /
[doit, DOE, zookeeper]
[zk: linux02:2181(CONNECTED) 12] ls /
[doit, DOE, zookeeper]
– 4 更新数据 set
[zk: localhost:2181(CONNECTED) 5] set /DOE DOE36
– 5 删除节点 delete path 只能删除没有子节点的节点 rmr递归删
[zk: localhost:2181(CONNECTED) 18] delete /NN
Node not empty: /NN
[zk: localhost:2181(CONNECTED) 18] rmr /NN

– 6 history
history
[zk: linux02:2181(CONNECTED) 24] history
14 - get /user
15 - set /user hangge
16 - get /user
17 - set /user ODIT
18 - get /user
19 - create /user/vip hangge
20 - get /user
21 - get /user/vip
22 - delete /user/vip
23 - get /user/vip
24 - history
zookeeper除了可以保存数据 , 读取数据以外还有一个非常重要的功能 , 就是监听通知
•监听节点数据的变化
•监听子节点个数的变化
Bash
ls path [ls2] [watch] 监听子节点个数的变化
get path [watch] 监听节点数据的变化
注意: 在脚本命令中监控只监听一次

当DOE节点的数据发生变化以后 会别通知
WATCHER:
WatchedEvent state:SyncConnected type:NodeDataChanged path:/DOE
监听子节点个数的变化

WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/

3.2 java API(了解)

创建maven项目, 添加zk依赖
XML

<!--添加zookeeper依赖-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.6</version>
</dependency>
public class Test1 {
	static ZooKeeper zk = null;
	static {
		try {
			zk = new ZooKeeper("doit001:2181,doit002:2181,doit003:2181", 2000, null);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 获取指定节点的数据
	 * @param args
	 * @throws Exception
	 */
	public static void getNodeData() throws KeeperException, InterruptedException {
		byte[] data = zk.getData("/doit12", null, null);
		System.out.println(new  String(data));
	}

	/**
	 * 获取指定路径下的所有的子节点  获取值
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public  static void   getData2() throws KeeperException, InterruptedException {
		// 遍历/ 节点  获取所有的子节点   
		List<String> list = zk.getChildren("/", null);
		//有子节点
		if(list!=null && list.size()>0) {
			// 获取所有子节点的值
			for (String name : list) {
				byte[] data = zk.getData("/"+name, null, null);
				System.out.println(name+":"+new String(data));
			}
		}
	}

	public static  void  createNode() throws Exception {

		/**

		 * 参数一  节点名称  注意  /开头
		 * 参数二  节点的数据值
		 * 参数三  权限
		 * 参数四  节点的类型  永久节点  临时节点      有序节点   无序节点
		 *    PERSISTENT 默认的永久节点
		 *    PERSISTENT_SEQUENTIAL   永久有序
		 *    EPHEMERAL   临时节点  当客户端连接断开以后 节点会自动删除  zk.close()
		 *    EPHEMERAL_SEQUENTIAL   临时有序的
		 */
		// 如果创建节点成功  返回当前节点名字
		String name = zk.create("/aaa", "haha".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
		System.out.println(name);
	}

	/**
	 * 更新节点数据
	 * @throws KeeperException
	 * @throws InterruptedException
	 */
	public  static  void  setData() throws KeeperException, InterruptedException {
		 // 更新数据   如果返回的stat!=null 说明成功
		Stat stat = zk.setData("/aaa0000000014", "heihei".getBytes(), -1) ;
	}

	/**
	 * 删除空节点
	 * @throws Exception
	 */
	public static void   deleteEmptyNode() throws Exception {
		// 删除jiedain  删除没有子节点的节点
		zk.delete("/doit", -1);
	}
	/**
	 * 删除任意节点
	 *    1  遍历当前要删除的节点 
	 *    2  递归删除
	 * @throws Exception
	 *
	 */
	public static void   delete2(String path) throws Exception {
		// 判断是否有子节点  /a/b/c
		List<String> list = zk.getChildren(path, null);
		if(list!=null && list.size()>0) {  //有子节点
			for (String child : list) {
				delete2(path+"/"+child);
			}
		}
		zk.delete(path, -1);
	}

	public static void main(String[] args) throws Exception {
		//getZkConnection();
		delete2("/doit") ;
	}
}

3.3 监控

/**

  • 连接监控

  • 在获取zk的连接对象的时候指定一次连接监控

  •  没有任何类型  初始化 的工作
    
  • @author ThinkPad

*/

public class DemoWatcher1 {

public static void main(String[] args) throws Exception {

	ZooKeeper zk = new ZooKeeper("doit001:2181", 2000, new Watcher() {

		// 连接成功以后执行  和 节点的变化监控没关系

		@Override

		public void process(WatchedEvent event) {

			// TODO Auto-generated method stub

			System.out.println("连接成功.....");

		}

	}) ;

	zk.close();

}

}

单次监控子节点变化
public class ChangeChildrenWatcher {

public static void main(String[] args) throws Exception {

	// 获取连接对象

	ZooKeeper zk = new ZooKeeper("doit001:2181", 2000, null);

	 // 监控只监控一次

	zk.getChildren("/", new Watcher() {

		// 当监控的目录 /  的子节点的个数发生变化的时候会被执行

		@Override

		public void process(WatchedEvent event) {

			System.out.println(event.getType()+":"+event.getPath());

			System.out.println("/ 目录下的子节点发生变化了.....");

		}

	});

	

	// 阻塞

	Thread.sleep(Integer.MAX_VALUE);

	

	zk.close();

}

}

一直监控
/**

  • 监控一个节点 当节点的子节点发生变化的时候重新获取子节点
  • @author ThinkPad

*/

public class ChangeChildrenWatcher2 {
public static void main(String[] args) throws Exception {
// 获取连接对象
ZooKeeper zk = new ZooKeeper(“doit001:2181”, 2000, null);
// 监控只监控一次
List list = zk.getChildren(“/”, new Watcher() {
// 当监控的目录 / 的子节点的个数发生变化的时候会被执行
@Override
public void process(WatchedEvent event) {
// 节点变化的时候
System.out.println(event.getType() + “:” + event.getPath());
// 再次注册监控
try {
// 注意this 代表的是上面的哪个匿名内部类 process()方法
List children = zk.getChildren(“/”, this);
for (String string : children) {
System.out.println(string);
}

			} catch (KeeperException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	});
	for (String string : list) {
		System.out.println(string);
	}
	// 阻塞
	Thread.sleep(Integer.MAX_VALUE);
	zk.close();
}

}

监控节点数据发生变化
public class DataChangerWatcher {

public static void main(String[] args) throws Exception {

	ZooKeeper zk = new ZooKeeper("doit001:2181", 2000, null);

	zk.getData("/doit12", new Watcher() {

		@Override

		public void process(WatchedEvent event) {

	      System.out.println(event.getType()+"---"+event.getPath());

	      try {

	    	  // 再次注册

			byte[] data = zk.getData("/doit12", this, null);

			// 打印新值

			System.out.println(new String(data));

		} catch (KeeperException | InterruptedException e) {

			// TODO Auto-generated catch block

			e.printStackTrace();

		}

		}

	}, null) ;

	

	Thread.sleep(Integer.MAX_VALUE);

	zk.close();

}

}

3.4 综合案例

模拟集群服务器上线下线感知
/**

  • 服务器上线向zk中注册节点
  • @author ThinkPad

*/
public class Servers {
static ZooKeeper zk = null ;
// 1 获取连接
public static void getConnection(String hostname) throws IOException {
zk = new ZooKeeper(“doit001:2181”, 2000, new Watcher() {
@Override
public void process(WatchedEvent event) {
System.out.println(“你的宝宝上线了…”+hostname);

		}
	});
}

// 2 注册节点  (添加节点)
public  static void  register(String hostname) throws KeeperException, InterruptedException {
	// 注册节点
	zk.create("/servers/server", hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}

// 3 对外服务

public static  void  service(String hostname) throws InterruptedException {
	System.out.println(hostname+"正在服务.......");
	Thread.sleep(Integer.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
	getConnection(args[0]);
	register(args[0]) ;
	service(args[0]);
}

}

客户端
/**

  • 获取现在正在服务的所有的节点 当节点的数量发生变化的时候重新获取服务列表
  • @author ThinkPad

*/
public class Clients {
static ZooKeeper zk = null;

// 1 获取连接
public static void getConnection() throws IOException {
	zk = new ZooKeeper("doit001:2181", 2000, new Watcher() {

		@Override
		public void process(WatchedEvent event) {
			System.out.println("发现一个客户.......");
		}
	});
}

// 2 获取服务列表
public static void getServersList() throws Exception, InterruptedException {

	List<String> children = zk.getChildren("/servers", new Watcher() {
       // 当servers下面的节点发生变化的时候执行一次
		@Override
		public void process(WatchedEvent event) {
			// 重新遍历servers下面的节点
			try {
				List<String> children2 = zk.getChildren("/servers", this);
				List<String> hosts =  new ArrayList<>() ;
				for (String string : children2) {
					byte[] data = zk.getData("/servers/"+string, null, null);
					hosts.add(new String(data));
				}
				System.out.println(hosts);
				
			} catch (KeeperException | InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	});

	List<String> list = new ArrayList<String>();
	for (String string : children) {
		byte[] data = zk.getData("/servers/" + string, null, null);
		list.add(new String(data));
	}
	System.out.println(list);

}

// 3 处理业务
public static void service() throws InterruptedException {
	System.out.println("干活...........");
	Thread.sleep(Integer.MAX_VALUE);
}
public static void main(String[] args) throws Exception {
	getConnection();
	getServersList();
	service();
}

}

Zookeeper是什么?
Zookeeper基本的功能?
Zookeeper的使用场景?
Zookeeper数据组织结构 ?
Zookeeper核心角色?
Zookeeper的选举机制 ?
Zookeeper操作数据? Shell java
Zookeeper启停和状态查看 ?

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值