加粗样式
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 子节点数量
[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
[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 #查看发现已经消失
[]
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"
[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 / #查看节点状态