Zookeeper入门到实战:概念、集群、选举机制与常用命令全解析
前言
在学习Flink、Kafka、HBase等大数据组件的过程中,Zookeeper几乎是绕不开的基础组件。它在集群中负责协调各节点的状态、配置同步、服务注册等工作。本文基于尚硅谷Zookeeper V3.3教程,系统梳理Zookeeper的核心知识点,帮你真正搞清楚Zookeeper到底是什么、怎么用、面试怎么答。
一、Zookeeper是什么
Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。
用一句话概括:Zookeeper = 文件系统 + 通知机制
从设计模式角度理解,Zookeeper是一个基于观察者模式设计的分布式服务管理框架:
- 负责存储和管理大家都关心的数据
- 接受观察者(客户端)的注册
- 一旦数据状态发生变化,通知已注册的观察者做出响应
这个机制听起来很抽象,举个实际例子就清楚了:
服务端启动时 → 向Zookeeper注册信息(创建临时节点)
客户端 → 获取在线服务器列表,并注册监听
某服务端下线 → Zookeeper通知所有客户端
客户端收到通知 → 重新获取服务器列表,再次注册监听
二、Zookeeper的特点
Leader + Follower集群结构:一个集群由一个Leader和多个Follower组成
半数存活即可服务:集群中只要有半数以上节点存活,就能正常提供服务。这也是为什么Zookeeper适合安装奇数台服务器(下文详细解释)
全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪个Server,读到的数据都是一致的
更新请求顺序执行:来自同一个Client的更新请求按发送顺序依次执行
数据更新原子性:一次数据更新要么全部成功,要么全部失败
实时性:在一定时间范围内,Client能读到最新数据
为什么要安装奇数台?
假设集群有3台,允许1台挂掉(1 < 3/2+1);如果安装4台,同样只允许1台挂掉,多花了1台服务器却没有提高容错能力。所以奇数台更划算。
生产经验参考:10台服务器装3台ZK,20台装5台,100台以上装11台。
三、数据结构
ZooKeeper的数据模型与Unix文件系统非常类似,整体可以看作是一棵树,每个节点称为一个ZNode。
/
├── znode1
│ ├── znode1/leaf1
│ └── znode1/leaf2
└── znode2
└── znode2/leaf
ZNode的特点:
- 每个ZNode默认能够存储1MB的数据
- 每个ZNode都通过其路径唯一标识,如/znode1/leaf1
- 每次写操作都有唯一的事务ID(zxid)
四、应用场景
Zookeeper在分布式系统中的应用非常广泛,主要有以下几类:
4.1 统一命名服务
在分布式环境下,对应用/服务进行统一命名,便于识别。
类比:IP地址不容易记住,但域名容易记住。可以将域名和IP的映射关系存储在Zookeeper中,客户端访问域名,Zookeeper返回对应的IP。
4.2 统一配置管理
分布式环境下配置文件同步非常常见,比如Kafka集群中所有节点的配置信息需要保持一致。
实现方式:
4.3 统一集群管理
实时掌握每个节点的状态:
4.4 服务器动态上下线
客户端能实时感知服务器的上下线变化,这也是Zookeeper最经典的应用场景之一,后面会有代码实现。
4.5 软负载均衡
在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求,实现动态负载均衡。
五、安装与配置
5.1 本地单机安装
安装步骤:
# 解压
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz -C /opt/module/
# 改名
mv apache-zookeeper-3.5.7-bin/ zookeeper-3.5.7
# 将zoo_sample.cfg改为zoo.cfg
mv zoo_sample.cfg zoo.cfg
# 修改dataDir路径(不要用默认的/tmp,容易被Linux清除)
vim zoo.cfg
# 修改为:dataDir=/opt/module/zookeeper-3.5.7/zkData
# 创建zkData目录
mkdir zkData
基本操作:
# 启动
bin/zkServer.sh start
# 查看状态
bin/zkServer.sh status
# 启动客户端
bin/zkCli.sh
# 退出客户端
quit
# 停止
bin/zkServer.sh stop
启动后用jps查看进程,能看到QuorumPeerMain说明Zookeeper已启动。
5.2 配置参数解读(zoo.cfg)
| tickTime | 2000 | 通信心跳时间(毫秒),客户端与服务器的心跳间隔 |
| initLimit | 10 | Leader和Follower初始连接时能容忍的最多心跳数 |
| syncLimit | 5 | Leader和Follower通信超时的最大心跳数,超过则认为Follower死掉 |
| dataDir | — | 数据存储目录,不要用默认的/tmp |
| clientPort | 2181 | 客户端连接端口,通常不修改 |
initLimit和syncLimit的实际时间都是心跳数乘以tickTime。例如syncLimit=5,tickTime=2000,则超时时间为10秒。
5.3 集群安装
集群规划(以3台为例):
| hadoop102 | 192.168.10.102 | 2 |
| hadoop103 | 192.168.10.103 | 3 |
| hadoop104 | 192.168.10.104 | 4 |
配置步骤:
# 1. 在zkData下创建myid文件,写入对应编号
echo 2 > /opt/module/zookeeper-3.5.7/zkData/myid
# hadoop103写3,hadoop104写4
# 2. 配置zoo.cfg,增加集群配置
vim zoo.cfg
zoo.cfg增加内容:
dataDir=/opt/module/zookeeper-3.5.7/zkData
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
参数解读:server.A=B:C:D
- A:服务器编号,与myid对应
- B:服务器地址
- C:Follower与Leader交换信息的端口(2888)
- D:Leader选举时服务器相互通信的端口(3888)
# 3. 同步配置到其他机器
xsync zoo.cfg
# 4. 分别在三台机器上启动
bin/zkServer.sh start
# 5. 查看各节点状态
bin/zkServer.sh status
# hadoop102: follower
# hadoop103: leader
# hadoop104: follower
5.4 集群启停脚本
每次手动逐台启停比较麻烦,可以写一个脚本统一管理:
#!/bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
echo "———- zookeeper $i 启动 ————"
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh start"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
echo "———- zookeeper $i 停止 ————"
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh stop"
done
};;
"status"){
for i in hadoop102 hadoop103 hadoop104
do
echo "———- zookeeper $i 状态 ————"
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh status"
done
};;
esac
保存为zk.sh,赋权后使用:
chmod u+x zk.sh
zk.sh start # 启动集群
zk.sh stop # 停止集群
zk.sh status # 查看状态
六、选举机制(面试重点)
Zookeeper的选举机制分两种场景:第一次启动和非第一次启动。
在理解选举之前,先记住三个关键概念:
| SID | 服务器ID,与myid一致,集群内唯一 |
| ZXID | 事务ID,标识一次状态变更,每次写操作递增 |
| Epoch | Leader任期代号,每完成一轮投票该值增加 |
6.1 第一次启动选举
以5台服务器(myid分别为1-5)为例,按顺序启动:
(1) 服务器1启动:发起一次选举,投自己1票。此时票数1票,不到半数(需要3票),状态保持LOOKING
(2) 服务器2启动:发起选举,服务器1和2各投自己一票,然后交换选票信息。服务器1发现服务器2的myid比自己大,更改选票为服务器2。此时:服务器1有0票,服务器2有2票,还不到半数,1、2状态保持LOOKING
(3) 服务器3启动:发起选举,服务器1和2都更改选票为服务器3。投票结果:服务器3有3票,超过半数,服务器3当选Leader。服务器1、2变为FOLLOWING,服务器3变为LEADING
(4) 服务器4启动:此时服务器1、2、3已不是LOOKING状态,不会更改选票。服务器4服从多数,变为FOLLOWING
(5) 服务器5启动:同服务器4,直接变为FOLLOWING
总结规律(第一次启动):谁的myid大,谁当Leader(前提是票数过半)
6.2 非第一次启动选举
触发条件:
- 服务器初始化启动
- 服务器运行期间无法和Leader保持连接
情况一:集群中已存在Leader
机器只需与Leader建立连接并同步状态即可,不需要选举。
情况二:集群中不存在Leader
此时选举规则优先级(从高到低):
举例:5台服务器组成集群,SID为1、2、3、4、5,ZXID为8、8、8、7、7,SID=3的是Leader。某时刻3和5出现故障,开始选举:
| 1 | 1 | 8 | 1 |
| 2 | 1 | 8 | 2 |
| 4 | 1 | 7 | 4 |
按规则:EPOCH相同→比ZXID,服务器1和2的ZXID=8大于服务器4的ZXID=7→再比SID,服务器2的SID大于服务器1→服务器2当选新Leader
七、客户端命令行操作
7.1 常用命令汇总
# 连接集群
bin/zkCli.sh -server hadoop102:2181
# 显示所有操作命令
help
# 查看节点内容
ls /
ls -s / # 附加详细信息
# 查看节点数据
get /path
get -s /path # 附加统计信息
get -w /path # 监听节点内容变化
# 创建节点
create /path "data" # 普通永久节点
create -s /path "data" # 带序号的永久节点
create -e /path "data" # 临时节点
create -e -s /path "data" # 带序号的临时节点
# 修改节点值
set /path "newdata"
# 查看节点状态
stat /path
# 删除节点
delete /path # 删除单个节点(有子节点时失败)
deleteall /path # 递归删除节点及其所有子节点
7.2 节点类型详解
ZNode分为四种类型:
| Persistent | 持久 | 无序 | 断开连接后节点依旧存在 |
| Persistent_Sequential | 持久 | 有序 | 节点名自动追加递增序号 |
| Ephemeral | 临时 | 无序 | 断开连接后节点自动删除 |
| Ephemeral_Sequential | 临时 | 有序 | 临时节点+自动序号 |
临时节点的典型应用:服务注册。服务器启动时创建临时节点注册自己,服务器宕机后与Zookeeper连接断开,临时节点自动删除,其他客户端通过监听感知到服务器下线。
有序节点的典型应用:分布式锁。利用序号的全局唯一性和单调递增性来判断锁的持有顺序。
7.3 节点详细数据字段
使用get -s /path或ls -s /path可以查看节点的详细信息:
| czxid | 创建节点的事务zxid |
| ctime | 节点被创建的时间(毫秒数,从1970年开始) |
| mzxid | 最后一次更新节点的事务zxid |
| mtime | 最后一次修改节点的时间 |
| pZxid | 最后一次更新子节点的zxid |
| cversion | 子节点变化号(子节点被修改次数) |
| dataVersion | 数据变化号 |
| aclVersion | 访问控制列表的变化号 |
| ephemeralOwner | 临时节点的session id,非临时节点为0 |
| dataLength | 数据长度 |
| numChildren | 子节点数量 |
7.4 监听器原理
监听器是Zookeeper实现通知机制的核心,原理如下:
客户端
├── Main线程
│ └── 创建ZooKeeper客户端
│ ├── connect线程 → 将监听事件注册到Zookeeper
│ └── listener线程 → 接收Zookeeper的通知,调用process()
│
Zookeeper服务端
└── 注册监听器列表 → 监听到数据/路径变化 → 通知listener线程
两种常用监听:
# 监听节点数据变化(值改变时触发)
get -w /path
# 监听子节点增减变化(子节点增加或删除时触发)
ls -w /path
**注意:Zookeeper的监听是一次性的!**注册一次只能监听一次。如果需要持续监听,每次收到通知后需要重新注册。
八、客户端向服务端写数据流程
Zookeeper的写操作分两种情况:
情况一:写请求发送给Leader
Client → Leader → 广播给所有Follower → 收到半数以上ACK → 提交写操作 → 通知所有Follower → 返回ACK给Client
情况二:写请求发送给Follower
Client → Follower → 转发给Leader → Leader广播给所有Follower → 半数以上ACK → 提交 → Follower通知Client
核心点:所有写操作最终都由Leader处理,Follower接到写请求会转发给Leader;只要半数以上节点写入成功,就返回成功。
九、面试常见问题
Q1:Zookeeper的选举机制是什么?
第一次启动:myid大的胜出(前提是票数过半);
非第一次启动:①EPOCH大的胜出 ②EPOCH相同ZXID大的胜出 ③都相同SID大的胜出
Q2:生产集群应该安装多少台Zookeeper?
安装奇数台:
- 10台服务器 → 3台ZK
- 20台服务器 → 5台ZK
- 100台以上 → 11台ZK
服务器数量多:好处是提高可靠性,坏处是提高通信延时。超过11台后收益递减。
Q3:Zookeeper的Watch机制是什么?
客户端注册监听关心的ZNode,当ZNode发生变化(数据改变/节点删除/子节点增减)时,Zookeeper通知客户端。监听是一次性的,触发一次后需要重新注册。
Q4:Zookeeper的常用命令有哪些?
ls、get、create、set、delete、deleteall、stat
小结
Zookeeper核心知识体系
├── 是什么:文件系统 + 通知机制,基于观察者模式的分布式协调服务
├── 数据结构:树形ZNode,每节点最多1MB,路径唯一标识
├── 集群特点:半数存活即可服务 → 安装奇数台
├── 选举机制(面试重点)
│ ├── 第一次启动:myid大的胜出
│ └── 非第一次启动:EPOCH → ZXID → SID 依次比较
├── 节点类型:持久/临时 × 有序/无序 = 4种
├── 监听机制:注册监听 → 数据变化 → 通知client → 需重新注册
└── 写数据流程:最终都由Leader处理,半数以上写入即成功
Zookeeper在大数据生态中是基础设施级别的组件,Kafka用它做集群协调(高版本Kafka已可脱离Zookeeper),HBase用它做Master选举和Region管理。真正理解了Zookeeper的工作机制,再看这些组件的原理会轻松很多。







