Zookeeper基础入门与安装部署详解
【1】Zookeeper基础入门① Zookeeper是什么Zookeeper是一个开源的分布式的、为分布式应用提供协调服务的Apache项目。Zookeeper从设计模式角度来理解,其实是一个基于观察者模式设计的分布式服务管理框架。它负责存储和管理大家都关心的数据,然后接受观察者的注册。一旦这些数据的状态发送变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出...
【1】概述
① 简介
Zookeeper是一个开源的分布式的、为分布式应用提供协调服务的Apache项目。
Zookeeper从设计模式角度来理解,其实是一个基于观察者模式设计的分布式服务管理框架。它负责存储和管理大家都关心的数据,然后接受观察者的注册。一旦这些数据的状态发送变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。从Zookeeper存储数据和通知机制来讲,可以说Zookeeper=文件系统+通知机制。
zookeeper实现服务器节点动态上下线过程描述如下:
- 服务端启动时去注册信息(创建的都是临时节点);
- 客户端获取到当前在线服务器列表,并且注册监听;
- 服务器节点下线;
- 服务器节点上下线事件通知(Zookeeper负责);
- 客户端收到监听通知则做出相应反应,比如重新获取在线服务器列表注册监听。
② 集群特点
集群如下图所示:
- Zookeeper集群是一个leader和多个follow组成的集群。
- 集群中只要半数以上节点存活,Zookeeper集群就能正常服务。
- 全局数据一致,每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的。
- 更新请求顺序进行,来自同一个Client的更新请求按其发送顺序依次执行。
- 数据更新原子性,一次数据更新要么成功,要么失败。
- 实时性,在一定时间范围内,Client能读到最新数据。
③ 数据结构
Zookeeper的数据模型的结构与UNIX文件系统很类似,整体上可以看做是一棵树,每个节点称做一个ZNode。每一个ZNode默认能够存储1MB的数据,每个ZNode可以通过其路径唯一标识。
如下图所示:
④ 应用场景
Zookeeper有诸多应用场景,如统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。
- 统一命名服务
在分布式环境下,经常需要对应用/服务进行统一命名,便于区别。例如IP不容易记住而域名容易记住。

- 统一配置管理
分布式环境下,配置文件同步非常常见。一般要求一个集群中,所有节点的配置信息是一致的,比如kafka集群。对配置文件修改后,希望能够快速同步到各个节点上。
配置管理可交由Zookeeper实现:可将配置信息写入Zookeeper上的一个ZNode;每个客户端服务器监听这个ZNode;一旦ZNode中的数据被修改,Zookeeper将通知各个客户端。

- 统一集群管理
分布式环境中,实时掌握每个节点的状态是很必要的,以便可以根据节点实时状态做出一些调整。
Zookeeper可以实现实时监控节点状态的变化:可以将节点信息写入Zookeeper的一个ZNode;监听这个ZNode可获取它的实时状态变化。

- 软负载均衡
在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。
⑤ 节点类型
Zookeeper提供一个多层级的节点命名空间(节点称为znode),每个节点都用一个以斜杠(/)分隔的路径表示,而且每个节点都有父节点(根节点除外),非常类似于文件系统。并且每个节点都是唯一的。
znode节点有四种类型:
- PERSISTENT:永久节点。客户端与zookeeper断开连接后,该节点依旧存在
- EPHEMERAL:临时节点。客户端与zookeeper断开连接后,该节点被删除
- PERSISTENT_SEQUENTIAL:永久节点、序列化。客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
- EPHEMERAL_SEQUENTIAL:临时节点、序列化。客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
创建这四种节点:
[zk: localhost:2181(CONNECTED) 0] create /aa test # 创建持久化节点
Created /aa
[zk: localhost:2181(CONNECTED) 1] create -s /bb test # 创建持久序列化节点
Created /bb0000000001
[zk: localhost:2181(CONNECTED) 2] create -e /cc test # 创建临时节点
Created /cc
[zk: localhost:2181(CONNECTED) 3] create -e -s /dd test # 创建临时序列化节点
Created /dd0000000003
[zk: localhost:2181(CONNECTED) 4] ls / # 查看某个节点下的子节点
[aa, bb0000000001, cc, dd0000000003, zookeeper]
[zk: localhost:2181(CONNECTED) 5] stat / # 查看某个节点的状态
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x5
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 5
[zk: localhost:2181(CONNECTED) 6] get /aa # 查看某个节点的内容
test
[zk: localhost:2181(CONNECTED) 11] delete /aa # 删除某个节点
[zk: localhost:2181(CONNECTED) 7] ls / # 再次查看
[bb0000000001, cc, dd0000000003, zookeeper]
⑥ 事件监听
事件监听:在读取数据时,我们可以同时对节点设置事件监听,当节点数据或结构变化时,zookeeper会通知客户端。当前zookeeper针对节点的监听有如下四种事件:
-
节点创建:
stat -w /xx当/xx节点创建时:NodeCreated
-
节点删除:
stat -w /xx当/xx节点删除时:NodeDeleted
-
节点数据修改:
get -w /xx当/xx节点数据发生变化时:NodeDataChanged
-
子节点变更:
ls -w /xx当/xx节点的子节点创建或者删除时:NodeChildChanged
【2】单击版Zookeeper下载与安装
官网地址:http://zookeeper.apache.org/

下载地址:https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/
① 本机模式安装Zookeeper
这里下载的是3.5.5版本,将其放到linux某个路径下(这里有个坑,请下载apache-zookeeper-3.5.5-bin.tar.gz,该包可以正常使用。apache-zookeeper-3.5.5.tar.gz是源码包):
注意,安装Zookeeper一定先安装好jdk,linux安装jdk可以参考如下博文:
Linux下源码安装jdk1.8 , CentOS7 下rpm安装jdk1.8 。
安装步骤如下:
- 解压
tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz
//或者指定解压目录
tar -zxvf apache-zookeeper-3.5.5-bin.tar.gz -C /home/softinstall
- 修改配置文件(把conf中的zoo_sample.cfg重命名为zoo.cfg)
cd apache-zookeeper-3.5.5/conf
mv zoo_sample.cfg zoo.cfg
//配置数据路径
mkdir -p /home/softinstall/apache-zookeeper-3.5.5/zkData
vim zoo.cfg

② 操作Zookeeper
- 启动Zookeeper服务端
[root@localhost apache-zookeeper-3.5.5-bin]# bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /opt/softinstall/apache-zookeeper-3.5.5-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost apache-zookeeper-3.5.5-bin]# jps
27368 QuorumPeerMain
27388 Jps
- 关闭zkserver
bin/zkServer.sh stop
- 查看服务状态
[root@localhost apache-zookeeper-3.5.5-bin]# bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/softinstall/apache-zookeeper-3.5.5-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: standalone //表示单机模式
- 重启Zookeeper服务
//进入目录下
bin/zkServer.sh restart
- 启动客户端
bin/zkCli.sh

- 查看根目录下内容
[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
- 退出客户端
[zk: localhost:2181(CONNECTED) 6] quit
【3】Zookeeper配置文件参数解读
配置文件主要参数如下:
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/softinstall/apache-zookeeper-3.5.5/zkData
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
① tickTime=2000
该参数含义为通信心跳时间,Zookeeper服务器端和客户端心跳时间,单位毫秒。2000表示2S一次心跳检测,也就是每个tickTime时间就会发送一个心跳。它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间(session的最小超时时间=2*tickTime)。
② initLimit=10
这个参数指定了在集群初始化期间,跟随者(Follower)跟领导者(Leader)之间建立连接的最大超时时间,单位是 tickTime 。
集群中的follower与leader之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中Zookeeper服务器连接到leader的时限。
10个心跳帧,10*2=20s是最开始通信时leader和follower最大延时时间。如果超过该时间,则认为二者断开通信。
③ syncLimit=5
集群正常启动后集群中leader与follower之间的最大响应时间单位。假如响应时间超过syncLimit*tickTime,leader认为follower死掉,从服务器列表中删除follower。
- 定义: 这个参数定义了跟随者处理领导者发送的信息的最大延迟时间,同样是以 tickTime 为单位。
- 作用: 如果跟随者处理消息的时间超过了 syncLimit,那么领导者将认为该跟随者已断开连接,并将其标记为不可用。
- 影响: 这个参数应该大于 tickTime,但小于 initLimit,以确保集群的稳定性。
④ clientPort=2181
客户端与服务端通信的端口号,默认为2181。
⑤ dataDir
zookeeper的数据目录。另外还有日志文件目录默认在../apache-zookeeper-3.5.5-bin/logs下。
【4】zookeeper常见命令
① ZK服务端命令
启动ZK服务
sh bin/zkServer.sh start
查看服务状态
sh bin/zkServer.sh status
停止ZK服务
sh bin/zkServer.sh stop
重启ZK服务
sh bin/zkServer.sh restart
② zk客户端命令
ZooKeeper命令行工具类似于Linux的shell环境使用它我们可以简单的对ZooKeeper进行访问,数据创建,数据修改等操作. 使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。
命令行工具的一些简单操作如下:
- 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
- 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
- 创建文件,并设置初始内容: create /zk “test” 创建一个新的 znode节点“ zk ”以及与它关联的字符串
- 获取文件内容:
get /zk确认 znode 是否包含我们所创建的字符串 - 修改文件内容:
set /zk "zkbak"对 zk 所关联的字符串进行设置 - 删除文件:
delete /zk将刚才创建的 znode 删除 - 退出客户端: quit
- 帮助命令: help
③ ZooKeeper 常用四字命令
ZooKeeper 支持某些特定的四字命令字母与其的交互。它们大多是查询命令,用来获取 ZooKeeper 服务的当前状态及相关信息。用户在客户端可以通过 telnet 或 nc 向 ZooKeeper 提交相应的命令
- 可以通过命令:
echo stat|nc 127.0.0.1 2181来查看哪个节点被选择作为follower或者leader - 使用
echo ruok|nc 127.0.0.1 2181测试是否启动了该Server,若回复imok表示已经启动。 echo dump| nc 127.0.0.1 2181,列出未经处理的会话和临时节点。echo kill | nc 127.0.0.1 2181,关掉serverecho conf | nc 127.0.0.1 2181,输出相关服务配置的详细信息。echo cons | nc 127.0.0.1 2181,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息。echo envi |nc 127.0.0.1 2181,输出关于服务环境的详细信息(区别于 conf 命令)。echo reqs | nc 127.0.0.1 2181,列出未经处理的请求。echo wchs | nc 127.0.0.1 2181,列出服务器 watch 的详细信息。echo wchc | nc 127.0.0.1 2181,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。echo wchp | nc 127.0.0.1 2181,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。
【5】常见API与用法
ZooKeeper的java客户端有:原生客户端、ZkClient、Curator框架(类似于redisson,有很多功能性封装)。
原生客户端依赖:
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.0</version>
</dependency>
常见API与用法:
public class ZkTest {
public static void main(String[] args) throws KeeperException, InterruptedException {
// 获取zookeeper链接
CountDownLatch countDownLatch = new CountDownLatch(1);
ZooKeeper zooKeeper = null;
try {
zooKeeper = new ZooKeeper("127.0.0.1:2181", 30000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (Event.KeeperState.SyncConnected.equals(event.getState())
&& Event.EventType.None.equals(event.getType())) {
System.out.println("获取链接成功。。。。。。" + event);
countDownLatch.countDown();
}
}
});
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
创建一个节点,1-节点路径 2-节点内容 3-节点的访问权限 4-节点类型:
// 创建一个节点,1-节点路径 2-节点内容 3-节点的访问权限 4-节点类型
// OPEN_ACL_UNSAFE:任何人可以操作该节点
// CREATOR_ALL_ACL:创建者拥有所有访问权限
// READ_ACL_UNSAFE: 任何人都可以读取该节点
//永久节点
// zooKeeper.create("/jane/aa", "haha~~".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//临时节点
zooKeeper.create("/test", "haha~~".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
//永久序列化节点
// zooKeeper.create("/jane/cc", "haha~~".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
//临时序列化节点
// zooKeeper.create("/jane/dd", "haha~~".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
判断节点是否存在
Stat stat = zooKeeper.exists("/test", true);
if (stat != null){
System.out.println("当前节点存在!" + stat.getVersion());
} else {
System.out.println("当前节点不存在!");
}
判断节点是否存在,同时添加监听。需要注意的是,节点的监听事情是一次性事件。
zooKeeper.exists("/test", event -> {
});
获取一个节点的数据
// 获取一个节点的数据
byte[] data = zooKeeper.getData("/jane/ss0000000001", false, null);
System.out.println(new String(data));
查询一个节点的所有子节点
// 查询一个节点的所有子节点
List<String> children = zooKeeper.getChildren("/test", false);
System.out.println(children);
更新一个节点的内容
// 更新
zooKeeper.setData("/test", "wawa...".getBytes(), stat.getVersion());
删除一个节点
// 删除一个节点
//zooKeeper.delete("/test", -1);
断开链接
if (zooKeeper != null){
zooKeeper.close();
}
}
}
【6】监听服务器动态上下线实例
背景需求如下:某分布式系统中,主节点有多台,可能进行动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线状态。
客户端代码
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
public class DistributeClient {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
DistributeClient client = new DistributeClient();
// 1 获取zookeeper集群连接
client.getConnect();
// 2 注册监听
client.getChlidren();
// 3 业务逻辑处理
client.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void getChlidren() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren("/servers", true);
// 存储服务器节点主机名称集合
ArrayList<String> hosts = new ArrayList<String>();
for (String child : children) {
byte[] data = zkClient.getData("/servers/"+child, false, null);
hosts.add(new String(data));
}
// 将所有在线主机名称打印到控制台
System.out.println(hosts);
}
private String connectString="127.0.0.1:3181,127.0.0.1:3182,127.0.0.1:3183";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString , sessionTimeout , new Watcher() {
public void process(WatchedEvent event) {
try {
getChlidren();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
服务端代码
import java.io.IOException;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;
public class DistributeServer {
public static void main(String[] args) throws Exception {
DistributeServer server = new DistributeServer();
// 1 连接zookeeper集群
server.getConnect();
// 2 注册节点
server.regist(args[0]);
// 3 业务逻辑处理
server.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void regist(String hostname) throws KeeperException, InterruptedException {
//注意,创建的是临时、有序号节点。这样服务器down了节点也就不存在了
String path = zkClient.create("/servers/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(hostname +"is online ");
}
private String connectString="127.0.0.1:3181,127.0.0.1:3182,127.0.0.1:3183";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
private void getConnect() throws IOException {
zkClient = new ZooKeeper(connectString , sessionTimeout , new Watcher() {
public void process(WatchedEvent event) {
// TODO Auto-generated method stub
}
});
}
}
zookeeper更多使用参考博文:ZooKeeper学习之内部原理
更多推荐



所有评论(0)