简介
ZooKeeper 是一款分布式协调框架,它可以为分布式系统提供一致性服务。ZooKeeper 最初由 Yahoo 开发,后来捐献给了 Apache 基金会,现已成功 Apache 的顶级项目,目前在 Github 上有 9.5k+Star。
ZooKeeper 作为顶级分布式开源项目,应用非常广泛,Dubbo 和 Kafka 这些知名的开源项目都在使用。
分布式协调
要理解 ZooKeeper 我们首先需要了解下什么是分布式协调
?这里拿 Spring Cloud 中注册中心的例子来说吧。
微服务(分布式)系统中有很多服务,相同的服务又有多个实例,我们在应用中可以通过服务名来负载均衡地调用服务,而这些服务有可能会挂掉,也有可能会有新的实例加入。此时我们就需要一个东西来做协调,保存好服务名称和可用实例调用 IP 的对应关系,此时注册中心就是一个分布式协调者的角色,而 ZooKeeper 就可以用来充当这个协调者。
安装
ZooKeeper 的安装无论是 Windows 还是 Linux 都是很方便的,我们先来学习下它的安装。
Windows 安装
- 首先下载 ZooKeeper 安装包,下载地址:https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz
- 解压到指定目录,解压完成后目录结构如下;
- 在
conf
目录下创建配置文件zoo.cfg
,内容如下;
# 设置心跳时间,单位毫秒tickTime=2000# 存储内存数据库快照的文件夹dataDir=I:/developer/env/apache-zookeeper-3.7.0-bin/data# 监听客户端连接的端口clientPort=2181
- 进入
bin
目录,启动 ZooKeeper 服务;
zkServer.cmd<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 服务启动成功后,控制台会输出如下信息。
Linux 安装
- 使用 Docker 安装 ZooKeeper 无疑是最方便的,首先我们下载它的 Docker 镜像;
docker pull zookeeper:3.7.0<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 创建好 ZooKeeper 的配置文件目录,并切换到该目录创建配置文件
zoo.cfg
;
mkdir /mydata/zookeeper/conf/ -pcd /mydata/zookeeper/conf/touch zoo.cfg
- 配置文件
zoo.cfg
内容如下,直接使用 VIM 编辑即可;
# 设置心跳时间,单位毫秒tickTime=2000# 存储内存数据库快照的文件夹dataDir=/tmp/zookeeper# 监听客户端连接的端口clientPort=2181
- 运行 ZooKeeper 容器。
docker run -p 2181:2181 --name zookeeper \<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">-v /mydata/zookeeper/conf/zoo.cfg:/conf/zoo.cfg \<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">-d zookeeper:3.7.0<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
命令行操作
接下来我们用命令行来操作下 ZooKeeper,熟悉下 ZooKeeper 的使用。
- 首先使用
zkCli
命令行工具连接到 ZooKeeper;
zkCli.cmd -server 127.0.0.1:2181<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 通过
help
可以命令查看 ZooKeeper 的常用命令;
[zk: 127.0.0.1:2181(CONNECTED) 0] helpZooKeeper -server host:port -client-configuration properties-file cmd args addWatch [-m mode] path # optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE addauth scheme auth close config [-c] [-w] [-s] connect host:port create [-s] [-e] [-c] [-t ttl] path [data] [acl] delete [-v version] path deleteall path [-b batch size] delquota [-n|-b|-N|-B] path get [-s] [-w] path getAcl [-s] path getAllChildrenNumber path getEphemerals path history listquota path ls [-s] [-w] [-R] path printwatches on|off quit reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*] redo cmdno removewatches path [-c|-d|-a] [-l] set [-s] [-v version] path data setAcl [-s] [-v version] [-R] path acl setquota -n|-b|-N|-B val path stat [-w] path sync path version whoami
- 大家都知道 Redis 是通过
key-value
的形式存储数据的,而 ZooKeeper 是通过znode-value
的形式存储数据的,znode 有点像目录,而/
目录就是 ZooKeeper 中的根目录,通过如下命令可以查看所有 znode;
[zk: 127.0.0.1:2181(CONNECTED) 1] ls /<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">[zookeeper]<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 创建一个 znode 叫做
/zk_test
,存储字符串my_data
,这用起来有点像 Redis;
[zk: 127.0.0.1:2181(CONNECTED) 2] create /zk_test my_data<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">Created /zk_test<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 查看所有 znode,可以看到
zk_test
这个 znode;
[zk: 127.0.0.1:2181(CONNECTED) 3] ls /<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">[zk_test, zookeeper]<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 获取 znode 中存储的数据;
[zk: 127.0.0.1:2181(CONNECTED) 4] get /zk_test<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">my_data<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
- 修改 znode 中的数据;
[zk: 127.0.0.1:2181(CONNECTED) 5] set /zk_test test_data[zk: 127.0.0.1:2181(CONNECTED) 6] get /zk_testtest_data
- 删除 znode 中的数据;
[zk: 127.0.0.1:2181(CONNECTED) 7] delete /zk_test<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">[zk: 127.0.0.1:2181(CONNECTED) 8] ls /<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">[zookeeper]<br data-darkmode-color-16243305138977="rgb(171, 178, 191)" data-darkmode-original-color-16243305138977="#fff|rgb(0,0,0)|rgb(171, 178, 191)" data-darkmode-bgcolor-16243305138977="rgb(49, 54, 63)" data-darkmode-original-bgcolor-16243305138977="#fff|rgb(40, 44, 52)">
可视化管理
PrettyZoo
是一款基于 Apache Curator 和 JavaFX 实现的 Zookeeper 图形化管理客户端。颜值很高,推荐使用。
- 首先下载
PrettyZoo
的安装包,下载地址:https://github.com/vran-dev/PrettyZoo/releases
- 我们需要创建一个连接,连接到 ZooKeeper,可以发现
PrettyZoo
是支持通过 SSH 通道连接的;
- 双击连接,我们可以查看到 ZooKeeper 中存储的数据,很清楚的发现,ZooKeeper 是按目录结构存储数据的;
- 右键目录,我们可以创建和删除 znode,有了这个工具,基本上可以和命令行操作说再见了;
- 如果你还是觉得命令行比较炫酷的话,
PrettyZoo
也实现了命令行功能,打开命令行标签就可以愉快地敲命令了。
节点类型
ZooKeeper 中的节点(znode)是有生命周期的,这取决于节点的类型。类型有主要有下面四种:
- 持久节点(Persistent):默认节点类型,节点创建后,会一直存在。
- 持久顺序节点(Persistent Sequential):具有持久节点特性,节点名称后会增加自增数字后缀。
- 临时节点(Ephemeral):临时存在,当创建节点的会话关闭时,节点被删除。
- 临时顺序节点(Ephemeral Sequential):具有临时节点特性,节点名称后会增加自增数字后缀。
如果你用命令行创建节点的话,顺序特性对应-s
选项,临时特性对应-e
选项,比如如下命令:
# 创建持久顺序节点create -s /test/seq segText# 创建临时节点create -e /test/tmp tmpText# 创建临时顺序节点create -s -e /test/seqTmp setTmpText
创建成功后显示如下:
如果你用PrettyZoo
来创建的话,只要勾选一个选项即可。
作为注册中心使用
CAP 是分布式架构中的重要理论,其包括一致性 (Consistency)、可用性(Availability) 和分区容忍性(Partition tolerance)。我们经常使用的 Eureka 支持 AP,而 ZooKeeper 支持 CP。接下来我们学习下 ZooKeeper 在 Spring Cloud 中作为注册中心的应用。
- ZooKeeper 作为注册中心使用,用法基本和 Eureka 和 Consul 相同,首先我们需要在
pom.xml
中添加 ZooKeeper 的服务发现组件;
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId></dependency>
- 之后修改配置文件
application.yml
,添加 ZooKeeper 相关配置;
spring: cloud: zookeeper: # zookeeper连接地址 connect-string: localhost:2181 discovery: # 作为服务注册 register: true # 注册时使用IP地址而不是hostname prefer-ip-address: true
- 这里还是使用《Spring Cloud 学习教程》中的例子,有两个服务
zookeeper-ribbon-service
和zookeeper-user-service
,前者通过 Ribbon 远程调用后者;
- 分别启动两个服务,我们通过
PrettyZoo
可以发现,当 ZooKeeper 作为注册中心时,注册服务的名称、IP、端口都被存储到了里面;
- 我们调用
zookeeper-ribbon-service
中的接口测试下,发现可以正常访问,接口地址:http://localhost:8301/user/1
- 如果这时候我们把
zookeeper-user-service
服务关掉的话,我们可以发现 ZooKeeper 会自动删除存储的数据;
- 由此可以看出,ZooKeeper 作为微服务的注册中心是通过临时节点来实现的,当服务上线时会向 ZooKeeper 中注册,当服务下线时会被 ZooKeeper 删除,保障了微服务的高可用。
总结
今天我们学习了下 ZooKeeper 的安装、可视化工具 PrettyZoo 的使用以及 ZooKeeper 在 Spring Cloud 中作为注册中心的应用。其实 ZooKeeper 在分布式系统中还有很多应用,比如说做分布式锁、实现选主功能、取代 UUID 来生成唯一 ID,大家感兴趣的话可以深入研究下!