加粗样式
ZooKeeper官网
ZooKeeper 是一种面向分布式应用程序的分布式开源协调服务,目前属于 Apache 维护。现分布式数据一致性解决方案中可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
ZooKeeper特点:
半数以上
节点就能存活整个集群,集群安装适合奇数
台服务器。数据模型和分层命名空间:
ZooKeeper 提供的命名空间与 Unix 文件系统的命名空间非常相似。名称是由斜杠( / )分隔的路径元素序列。每个节点称做一个 ZNode ,ZooKeeper 命名空间中的每个节点(ZNode)都由一个唯一路径标识。
ZooKeeper 简单数据模型:
可配置 Cluster
顺序访问
高性能更快速
Session 指的是 ZooKeeper 服务器与客户端会话。当客户端(Client) 通过 TCP 长连接 连接到 ZooKeeper 服务器时,Session 开始建立连接并通过心跳检测(tickTime)
与服务器保持有效会话。通过此连接,客户端(Client) 也可以向 ZooKeeper 服务端发送请求并接受响应,同时也可以接收到 Watch 事件的通知。
当由于服务器压力太大、网络故障或是客户端(Client) 主动断开连接等各种原因导致客户端连接断开时,只要在会话超时(SessionTimeout)
规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
在 Zookeeper 中,数据模型由数据节点(ZNode) 组成树形结构,ZNode 是一个跟 Unix 文件系统相似的节点,可以往这个节点存储或获取数据。每一个 ZNode 默认能够存储 1MB 的数据。
ZNode节点类型:
Watcher 就是 ZooKeeper 中常用的事件监听器,ZooKeeper 允许用户注册一些 Watcher,并且在一些事件特定触发时,ZooKeeper 会将通知发给客户端。该机制是 ZooKeeper 实现分布式协调服务的重要特性。
Zookeeper采用ACL(AccessControlLists)策略来进行权限控制,类似于 Unix 文件系统的权限控制。
Zookeeper 定义的5种权限。
命令
作用
create
创建子节点权限
read
查看节点数据和子节点列表的权限
write
更新节点的权限
delete
删除节点的权限
admin
设置节点ACL的权限
Paxos 算法
Paxos 算法缺点
Paxos 算法缺陷: 在网络复杂的情况下,一个应用 Paxos 算法的分布式系统,可能很久无法收敛,甚至陷入活锁的情况。
造成这种情况的原因是系统中有一个以上的 Proposer(提议者),多个 Proposer(提议者) 相互争夺 Acceptor(接收者),造成迟迟无法达成一致的情况。针对这种情况,一种改进的 Paxos 算法被提出:从系统中选出一个节点作为 Leader,只有 Leader 能够发起提案。这样,一次 Paxos 流程中只有一个 Proposer,不会出现活锁的情况。
Zab 协议
CAP理论
CAP理论告诉我们,一个分布式系统不可能同时满足以下三种。
这三个基本需求,最多只能同时满足其中的两项,因为P是必须的,因此往往选择就在CP或者AP中。
ZooKeeper保证的是CP
ZooKeeper 不能保证每次服务请求的可用性。
(注:在极端环境下,ZooKeeper 可能会丢弃一些请求,消费者程序需要重新请求才能获得结果)。所以说,ZooKeeper 不能保证服务可用性。进行 Leader 选举时集群都是不可用。
部署准备
IP地址
主机名
操作系统
ZooKeeper版本(官方下载地址)
JDK版本(国内下载地址)
192.168.10.1
ZooKeeper1
CentOS 7.4
apache-zookeeper-3.5.7-bin.tar.gz
jdk-8u291-linux-x64.tar.gz
192.168.10.2
ZooKeeper2
CentOS 7.4
apache-zookeeper-3.5.7-bin.tar.gz
jdk-8u291-linux-x64.tar.gz
192.168.10.3
ZooKeeper3
CentOS 7.4
apache-zookeeper-3.5.7-bin.tar.gz
jdk-8u291-linux-x64.tar.gz
本次采用 ZooKeeper3.5.7 版本,JDK1.8.0_291。官方依赖查询
JDK安装
[root@ZooKeeper1 ~]# mkdir /zk/
[root@ZooKeeper1 ~]# ls /zk
jdk-8u291-linux-x64.tar.gz
[root@ZooKeeper1 zk]# tar zxvf jdk-8u291-linux-x64.tar.gz
[root@ZooKeeper1 zk]# ls
jdk1.8.0_291 jdk-8u291-linux-x64.tar.gz
[root@ZooKeeper1 zk]# cat << EOF >> /etc/profile.d/jdk.sh
> JAVA_HOME=/zk/jdk1.8.0_291
> PATH=$JAVA_HOME/bin:$PATH
> export JAVA_HOME PATH
> EOF
[root@ZooKeeper1 zk]# source /etc/profile
[root@ZooKeeper1 zk]# java -version
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)
[root@ZooKeeper1 zk]# wget http://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz
[root@ZooKeeper1 zk]# tar zxvf apache-zookeeper-3.5.7-bin.tar.gz
[root@ZooKeeper1 zk]# ll
总用量 150636
drwxr-xr-x 6 root root 134 5月 12 19:43 apache-zookeeper-3.5.7-bin
-rw-r--r-- 1 root root 9311744 5月 12 19:43 apache-zookeeper-3.5.7-bin.tar.gz
drwxr-xr-x 8 root root 273 4月 8 2021 jdk1.8.0_291
-rw-r--r-- 1 root root 144935989 5月 12 19:21 jdk-8u291-linux-x64.tar.gz
[root@ZooKeeper1 zk]# mkdir apache-zookeeper-3.5.7-bin/data
[root@ZooKeeper1 zk]# cat <> apache-zookeeper-3.5.7-bin/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zk/apache-zookeeper-3.5.7-bin/data
clientPort=2181
EOF
配置注解:
tickTime
:通信心跳时间,ZooKeeper Server 与 Client 心跳时间,单位毫秒
。initLimit
:Leader 和 Follower 间初始通信时限,单位次数
,(Leader 和 Follower 初始连接时能容忍的最多心跳数(tickTime的数量))。syncLimit
:Leader 和 Follower 间同步通信时限,单位次数
(Leader 和 Follower 之间通信时间超过syncLimit * tickTime,Leader认为Follwer死掉并删除Follwer)。dataDir
:保存 ZooKeeper 内存数据库中的快照信息(当未配置 dataLogDir 参数时,日志信息也会存放到此目录)。clientPort
:ZooKeeper 监听的端口,用于客户端连接使用。ZooKeeper 状态查询
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkServer.sh start #启动
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkServer.sh restart #重启
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkServer.sh status #状态检查
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkServer.sh help #查看命令
ZooKeeper JMX enabled by default
Using config: /zk/apache-zookeeper-3.5.7-bin/bin/../conf/zoo.cfg
Usage: apache-zookeeper-3.5.7-bin/bin/zkServer.sh [--config ] {start|start-foreground|stop|restart|status|print-cmd}
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkCli.sh -server 127.0.0.1 #ZooKeeper客户端连接
集群角色
角色
作用
Leader
提供读写服务,保证集群事务处理顺序性以及集群内部各服务器调度
Follower
提供读写服务,并定期向 Leader 汇报自己的节点状态(同时也参加 过半写成功的策略
和 Leader 的选举
)
Observer
提供读写服务,并定期向 Leader 汇报自己的节点状态(与 Follower 区别是不参与选举于策略,可以在不影响写性能的情况下提升集群的读性能)
Looking
寻找 Leader 状态,处于该状态需要进入选举流程
1、将 JDK 与 ZooKeeper 传给另外两台
[root@ZooKeeper1 zk]# for i in {2,3}
do
scp -r /zk/{jdk1.8.0_291,apache-zookeeper-3.5.7-bin} [email protected].$i:/zk/
done
[root@ZooKeeper2 zk]# source /etc/profile #生效
[root@ZooKeeper2 zk]# jps
15505 Jps
[root@ZooKeeper3 zk]# source /etc/profile #生效
[root@ZooKeeper3 zk]# jps
15513 Jps
2、将 ZooKeeper 配置传给另外两台
[root@ZooKeeper1 zk]# cat << EOF >> apache-zookeeper-3.5.7-bin/conf/zoo.cfg
> server.1=192.168.10.1:2888:3888
> server.2=192.168.10.2:2888:3888
> server.3=192.168.10.3:2888:3888
> EOF
[root@ZooKeeper1 zk]# for i in {2,3}
do
scp /zk/apache-zookeeper-3.5.7-bin/conf/zoo.cfg [email protected].$i:/zk/apache-zookeeper-3.5.7-bin/conf/
done
3、创建 myid 文件
[root@ZooKeeper1 zk]# echo "1" > /zk/apache-zookeeper-3.5.7-bin/data/myid
[root@ZooKeeper2 zk]# echo "2" > /zk/apache-zookeeper-3.5.7-bin/data/myid
[root@ZooKeeper3 zk]# echo "3" > /zk/apache-zookeeper-3.5.7-bin/data/myid
配置参数解读
server.A=B:C:D
A
:是一个数字,表示这个是第几台服务器,在 dataDir
目录下;需要确保每台服务器 myid
不重复,并且和本机的 zoo.cfg
中的 server.id
的 id
一样。B
:服务器地址。C
:是服务器中的 Follower 与集群中的 Leader 服务器交换信息的端口。D
:是集群中的 Leader 服务器宕机,需要用此端口来重新选举出一个新的 Leader,此端口就是用来执行选举时服务器相互通信的端口。4、启动 ZooKeeper 集群
[root@ZooKeeper1 zk]# for i in {1,2,3}
do
ssh [email protected].$i /zk/apache-zookeeper-3.5.7-bin/bin/zkServer.sh start
done
5、创建 ZooKeeper 状态查询脚本
#!/bin/bash
case $1 in
"start"){
for i in {1,2,3}
do
echo ---------- ZooKeeper $i 启动 ------------
ssh [email protected].$i "/zk/apache-zookeeper-3.5.7-bin/bin/zkServer.sh start"
done
};;
"stop"){
for i in {1,2,3}
do
echo ---------- ZooKeeper $i 停止 ------------
ssh [email protected].$i "/zk/apache-zookeeper-3.5.7-bin/bin/zkServer.sh stop"
done
};;
"status"){
for i in {1,2,3}
do
echo ---------- ZooKeeper $i 状态 ------------
ssh [email protected].$i "/zk/apache-zookeeper-3.5.7-bin/bin/zkServer.sh status"
done
};;
"restart"){
for i in {1,2,3}
do
echo ---------- ZooKeeper $i 重启 ------------
ssh [email protected].$i "/zk/apache-zookeeper-3.5.7-bin/bin/zkServer.sh restart"
done
};;
esac
ZooKeeper 选举机制
SID
:服务器ID。用来唯一标识每台 ZooKeeper 集群中的机器,每台机器不能重复,和 myid
一致。ZXID
:事务ID。ZXID 是一个事务 ID,用来标识每一次服务器状态的变更
。在某一时刻,集群中的每台机器的 ZXID 值不一定完全一致,这和 ZooKeeper 服务器对于客户端更新请求
的处理逻辑有关。Epoch
:每个 Leader 任期的代号。没有 Leader 时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加。非第一次启动选举 Leader 规则
ZooKeeper 操作
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkCli.sh -server 127.0.0.1:2181 #启动 ZooKeeper 客户端
[zk: 127.0.0.1:2181(CONNECTED) 0] help #显示所有命令使用
[zk: 127.0.0.1:2181(CONNECTED) 32] ls / #查看当前 ZNode 中所包含的内容
[zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 33] ls -s / #查看当前 ZNode 详细数据
[zookeeper]cZxid = 0x0 #cZxid:创建节点的事务 zxid
ctime = Thu Jan 01 08:00:00 CST 1970 #ctime:ZNode 被创建的毫秒数(默认从 1970 年开始)
mZxid = 0x0 #mZxid:ZNode 最后更新的事务 zxid
mtime = Thu Jan 01 08:00:00 CST 1970 #mtime:ZNode 最后修改的毫秒数(默认从 1970 年开始)
pZxid = 0x10000000f #pZxid:ZNode 最后更新的子节点 zxid
cversion = 1 #cversion:ZNode 子节点变化号,ZNode 子节点修改次数
dataVersion = 0 #dataVersion:ZNode 数据变化号
aclVersion = 0 #aclVersion:ZNode 访问控制列表的变化号
ephemeralOwner = 0x0 #ephemeralOwner:如果是临时节点,这个是 ZNode 拥有者的 Session id。如果不是临时节点则是 0。
dataLength = 0 #dataLength:ZNode 的数据长度
numChildren = 1 #numChildren:ZNode 子节点数量
1、分别创建2个普通节点(永久节点 + 不带序号)
[zk: 127.0.0.1:2181(CONNECTED) 9] create /xy "xy" #创建永久节点
Created /xy
[zk: 127.0.0.1:2181(CONNECTED) 10] create /xy/xy1 "xy1"
Created /xy/xy1
[zk: 127.0.0.1:2181(CONNECTED) 11] set /xy/xy1 "xy111111111111111111111111111" #修改值
[zk: 127.0.0.1:2181(CONNECTED) 12] get /xy/xy1 #查看值
xy111111111111111111111111111
2、获得节点的值
[zk: 127.0.0.1:2181(CONNECTED) 46] get /xy #获取节点值
xy
[zk: 127.0.0.1:2181(CONNECTED) 47] get /xy/xy1
xy1
[zk: 127.0.0.1:2181(CONNECTED) 48] get -s /xy/xy1 #查看 ZNode 详细信息
xy1
cZxid = 0x100000012
ctime = Fri May 13 23:06:07 CST 2022
mZxid = 0x100000012
mtime = Fri May 13 23:06:07 CST 2022
pZxid = 0x100000012
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
3、创建带序号的节点(永久节点 + 带序号)
[zk: 127.0.0.1:2181(CONNECTED) 14] create /xy2 #创建普通节点
Created /xy2
[zk: 127.0.0.1:2181(CONNECTED) 15] create -s /xy2/xy22 "xy22" #永久带序号 ZNode
Created /xy2/xy220000000000
[zk: 127.0.0.1:2181(CONNECTED) 16] create -s /xy2/xy22 "xy22"
Created /xy2/xy220000000001
[zk: 127.0.0.1:2181(CONNECTED) 17] create -s /xy2/xy22 "xy22"
Created /xy2/xy220000000002
4、创建短暂节点(短暂节点 + 不带序号 & 带序号)
[zk: 127.0.0.1:2181(CONNECTED) 20] create /xy3 "xy3" #创建永久节点
Created /xy3
[zk: 127.0.0.1:2181(CONNECTED) 21] create -e /xy3/xy33 "xy33" #创建临时节点
Created /xy3/xy33
[zk: 127.0.0.1:2181(CONNECTED) 22] create -e -s /xy3/xy33 "xy33" #创建临时带序号节点
Created /xy3/xy330000000001
[zk: 127.0.0.1:2181(CONNECTED) 23] ls /xy3 #查看临时节点
[xy33, xy330000000001]
[zk: 127.0.0.1:2181(CONNECTED) 24] quit #退出 ZooKeeper 客户端
[root@ZooKeeper1 zk]# apache-zookeeper-3.5.7-bin/bin/zkCli.sh -server 127.0.0.1:2181 #重新进入
[zk: 127.0.0.1:2181(CONNECTED) 0] ls /xy3 #查看发现已经消失
[]
5、监听节点值变化
192.168.10.1
[zk: 127.0.0.1:2181(CONNECTED) 0] get -w /xy
xy
[zk: 127.0.0.1:2181(CONNECTED) 1]
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/xy
192.168.10.2
[zk: 127.0.0.1:2181(CONNECTED) 1] set /xy "xyxyxy"
[zk: 127.0.0.1:2181(CONNECTED) 2] set /xy "xyxyxyxyxyxy"
6、节点删除与查看
[zk: 127.0.0.1:2181(CONNECTED) 7] delete /xy/xy1 #删除节点
[zk: 127.0.0.1:2181(CONNECTED) 8] deleteall /xy2 #递归删除
[zk: 127.0.0.1:2181(CONNECTED) 8] stat / #查看节点状态
7、ZooInspector 工具介绍
连接 ZooKeeper 地址方可操作,格式 IP:Port
连接上 ZooKeeper 的增删改查方可图形化执行