一、ZooKeeper介绍
1、ZooKeeper是什么
ZooKeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。ZooKeeper可以保证如下分布式一致性特性
1)顺序一致性
从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到ZooKeeper中去
2)原子性
所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群所有机器都成功应用了某一个事务,要么都没有应用
3)单一视图
无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的
4)可靠性
一旦服务端成功地应用了一个事务,并完成对客户端的响应,那么该事务所引起的服务端状态变更将会一直保留下来,除非有另一个事务又对其进行了变更
5)实时性
ZooKeeper保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态
2、ZooKeeper的设计目标
ZooKeeper致力于提供一个高性能、高可用且具有严格的顺序访问控制能力的分布式协调服务
1)简单的数据模型
ZooKeeper使得分布式程序能够通过一个共享的、树型结构的名字空间来进行相互协调。这里所说的树型结构的名字空间是指ZooKeeper服务器内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系就像文件系统的目录结构一样。不过和传统的磁盘文件系统不同的是,ZooKeeper将全量数据存储在内存中,以此来实现提高服务器吞吐、减少延迟的目的
2)可以构建集群
组成ZooKeeper集群的每台机器都会在内存中维护当前的服务器状态,并且每台几区之间都互相保持着通信。只要集群中存在超过一半的机器能够正常工作,那么整个集群就能够正常对外服务
ZooKeeper的客户端程序会选择和集群中任意一台集群共同来创建一个TCP连接,而一旦客户端和某台ZooKeeper服务器之间的连接断开后,客户端会自动连接到集群中的其他几区
3)顺序访问
对于来自客户端的每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序,应用程序可以使用ZooKeeper的这个特性来实现更高层次的同步原语
4)高性能
由于ZooKeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景
3、ZooKeeper的基本概念
1)集群角色
ZooKeeper中存在Leader、Follower和Observer三种角色。ZooKeeper集群中的所有机器通过一个Leader选举过程来选定一台被称为Leader的机器,Leader服务器为客户端提供读和写服务。除Leader外,其他机器包括Follower和Observer。Follower和Observer都能够提供读服务,唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的过半写成功策略,因此Observer可以在不影响写性能的情况下提升集群的读性能
2)会话(Session)
在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。ZooKeeper对外的服务端口默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接收响应,同时还能够通过连接接收到来自服务器的Watcher事件通知。Session的sessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够主动连接上集群中任意一台服务器,那么之前创建的会话依然有效
3)数据节点(ZNode)
ZooKeeper将所有数据存储在内存中,数据模型是一棵树,由/
进行分割的路径,就是一个ZNode。每个ZNode上都会保存自己的数据内容,同时还存保存一系列属性信息
在ZooKeeper中,ZNode可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在ZooKeeper上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。另外,ZooKeeper还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,ZooKeeper会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字
4)版本
ZooKeeper的每个ZNode上都会存储数据,对应于每个ZNode,ZooKeeper都会为其维护一个叫做Stat的数据结构,Stat中记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本),cversion(当前ZNode子节点的版本)和aversion(当前ZNode的ACL版本)
5)Watcher
ZooKeeper允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZooKeeper服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性
6)ACL
ZooKeeper采用ACL策略来进行权限控制,定义了如下5中权限
- CREATE:创建子节点的权限
- READ:获取节点数据和子节点列表的权限
- WRITE:更新节点数据的权限
- DELETE:删除子节点的权限
- ADMIN:设置节点ACL的权限
CREATE和DELETE这两种权限都是针对子节点的权限控制
二、使用ZooKeeper
1、部署ZooKeeper
1)、单机模式
ZooKeeper使用Java语言编写,因此它的运行环境需要Java环境的支持,Linux操作系统安装JDK可以参考https://blog.csdn.net/qq_40378034/article/details/86685680
ZooKeeper下载地址:https://archive.apache.org/dist/zookeeper/
[root@aliyun zookeeper]# wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.12/zookeeper-3.4.12.tar.gz
[root@aliyun zookeeper]# tar -zxf zookeeper-3.4.12.tar.gz
ZooKeeper目录结构如下:
初次使用ZooKeeper,需要将conf目录下的zoo_sample.cfg文件重命名为zoo.cfg,并且进行简单配置即可:
[root@aliyun zookeeper-3.4.12]# cd conf/
[root@aliyun conf]# cp zoo_sample.cfg zoo.cfg
[root@aliyun conf]# vi zoo.cfg
# ZooKeeper服务器心跳时间,单位为ms
tickTime=2000
# 投票选举新leader的初始化时间
initLimit=10
# leader与follower心跳检测最大容忍时间,响应超过syncLimit*tickTime,leader认为follower死掉,从服务器列表中删除follower
syncLimit=5
# 数据目录
dataDir=/tmp/zookeeper/data
# 日志目录
dataLogDir=/tmp/zookeeper/log
# ZooKeeper对外服务端口
clientPort=2181
创建dataDir和dataLogDir
[root@aliyun conf]# mkdir -p /tmp/zookeeper/data
[root@aliyun conf]# mkdir -p /tmp/zookeeper/log
在dataDir所配置的目录下,创建一个名为myid的文件,并写入一个数值,比如0。myid文件里存放的是服务器的编号
ZooKeeper的bin目录下有几个脚本,可以用这些脚本来启动与停止ZooKeeper服务
脚本 | 说明 |
---|---|
zkCleanup | 清理ZooKeeper历史数据,包括事务日志文件和快照数据文件 |
zkCli | ZooKeeper的一个简易客户端 |
zkEnv | 设置ZooKeeper的环境变量 |
zkServer | ZooKeeper服务器的启动、停止和重启脚本 |
启动ZooKeeper服务
[root@aliyun bin]# ./zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
通过./zkServer.sh status
命令查看ZooKeeper服务状态
[root@aliyun bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: standalone
停止服务
[root@aliyun bin]# ./zkServer.sh stop
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
Stopping zookeeper ... STOPPED
2)、伪集群模式
三个节点的zoo.cfg配置如下:
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper01/data
dataLogDir=/tmp/zookeeper01/log
clientPort=2181
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper02/data
dataLogDir=/tmp/zookeeper02/log
clientPort=2182
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper03/data
dataLogDir=/tmp/zookeeper03/log
clientPort=2183
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890
分别在三个${dataDir}目录下创建一个myid文件,并分别写入1、2、3。myid文件里存放的是服务器的编号
server.A=B:C:D。其中A是一个数字,被称为Server ID,用来标识该机器在集群中的机器序号,就是myid文件里面的值,集群中每台服务器的编号都必须唯一。B代表服务器的IP地址。C表示服务器与集群中的leader服务器交换信息的端口。D表示选举时服务器相互通信的端口
分别在三个节点的bin目录下使用./zkServer.sh start
启动
使用./zkServer.sh status
查看各自节点的状态,一个leader节点,两个follower节点
[root@aliyun bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper01/bin/../conf/zoo.cfg
Mode: follower
[root@aliyun bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper02/bin/../conf/zoo.cfg
Mode: leader
[root@aliyun bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper03/bin/../conf/zoo.cfg
Mode: follower
2、客户端脚本
进入ZooKeeper的bin目录之后,执行如下命令:
./zkCli.sh
当看到如下输出信息时,表示已经成功连接上本地的ZooKeeper服务器了:
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
上面的命令没有显式地指定ZooKeeper服务器地址,默认是连接本地的ZooKeeper服务器。如果希望连接指定的ZooKeeper服务器,可以通过如下方式实现:
./zkCli.sh -server ip:port
1)、创建
使用create命令,可以创建一个ZooKeeper节点。用法如下:
create [-s] [-e] path data acl
其中,-s或-e分别指定节点特性:顺序或临时节点。默认情况下,即不添加-s或-e参数时,创建的是持久节点
[zk: 127.0.0.1:2181(CONNECTED) 0] create /zk01 123
Created /zk01
执行完上面的命令,就在ZooKeeper的根节点下创建了一个叫作/zk01
的节点,并且节点的数据内容是123。另外,create命令的最后一个参数是acl,它是用来进行权限控制的,缺省情况下,不做任何权限控制
2)、读取
1)ls
使用ls命令,可以列出ZooKeeper指定节点下的所有子节点。当然,这个命令只能看到指定节点下第一级的所有子节点。用法如下:
ls path [watch]
其中,path表示的是指定数据节点的节点路径
执行如下命令:
[zk: 127.0.0.1:2181(CONNECTED) 1] ls /
[zk01, zookeeper]
第一次部署的ZooKeeper集群,默认在根节点/
下面有一个叫做/zookeeper
的保留节点
2)get
使用get命令,可以获取ZooKeeper指定节点的数据内容和属性信息。用法如下:
get path [watch]
执行如下命令:
[zk: 127.0.0.1:2181(CONNECTED) 2] get /zk01
123
cZxid = 0x10000000f
ctime = Tue Jul 30 22:57:10 CST 2019
mZxid = 0x10000000f
mtime = Tue Jul 30 22:57:10 CST 2019
pZxid = 0x10000000f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
上面的输出信息中,第一行是节点/zk01
的数据内容,其他几行则是创建该节点的事务ID(cZxid)、创建该节点的时间(ctime)、最后一次更新该节点的事务ID(mZxid)和最后一次更新该节点的时间(mtime)等属性信息
3)、更新
使用set命令,可以更新指定节点的数据内容。用法如下:
set path data [version]
其中,data就是要更新的新内容。set命令后面还有一个version参数,在ZooKeeper中,节点的数据是有版本概念的,这个参数用于指定本次更新操作是基于ZNode的哪一个数据版本进行的
执行如下命令:
[zk: 127.0.0.1:2181(CONNECTED) 3] set /zk01 456
cZxid = 0x10000000f
ctime = Tue Jul 30 22:57:10 CST 2019
mZxid = 0x100000011
mtime = Tue Jul 30 22:57:43 CST 2019
pZxid = 0x10000000f
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
执行完以上命令后,节点/zk01
的数据内容就已经被更新成456了。dataVersion的值由原来的0变成了1
4)、删除
使用delete命令,可以删除ZooKeeper上的指定节点。用法如下:
delete path [version]
此命令中的version参数和set命令中的version参数的作用是一致的
执行如下命令:
[zk: 127.0.0.1:2181(CONNECTED) 4] delete /zk01
执行完以上命令后,就可以把/zk01
这个节点成功删除了
需要注意的一点是,要想删除某一个指定节点,该节点必须没有子节点存在
[zk: 127.0.0.1:2181(CONNECTED) 5] create /zk01 123
Created /zk01
[zk: 127.0.0.1:2181(CONNECTED) 6] create /zk01/child 12345
Created /zk01/child
[zk: 127.0.0.1:2181(CONNECTED) 7] delete /zk01
Node not empty: /zk01
上面的这个输出结果表明了删除失败,通过Node not empty这个报错信息,可以看出无法删除一个包含子节点的节点