目录
[1. 持久节点](#1. 持久节点)
[2. 临时节点](#2. 临时节点)
[3. 有序节点](#3. 有序节点)
[4. 容器节点](#4. 容器节点)
[5. TTL节点](#5. TTL节点)
[1. 实现分布式锁](#1. 实现分布式锁)
[2. 乐观锁更新数据](#2. 乐观锁更新数据)
[1. 安装nc](#1. 安装nc)
[2. 开启nc](#2. 开启nc)
[3. 验证](#3. 验证)
[4. 其他用法](#4. 其他用法)
环境搭建
单机版搭建
- 官网下载zookeeper压缩包, 这里用3.8.3版本, 并解压
官网下载地址: Apache ZooKeeper
- 复制示例配置文件
cs
cp zoo_sample.cfg zoo.cfg
zoo.cfg配置里面参数说明, 不用修改也能启动
cs
# zookeeper基本时间单位,2妙
tickTime=2000
# follower初始化到leader最大时长, 表示tickTime的倍数, 也就是20秒
initLimit=10
# leader和follower同步数据的最大时长, 表示tickTime的倍数, 也就是10秒
syncLimit=5
# 数据和日志存储目录, 也可以配dataLogDir单独指定日志存储目录, 不建议放在默认的/tmp下
dataDir=/tmp/zookeeper
# 对客户端提供端口号
clientPort=2181
# 对单个客户端提供最大连接数
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# 保存数据的快照数量, 多余的会被清除
#autopurge.snapRetainCount=3
# 自动触发清除任务的时间间隔, 单位小时. 0表示不清除
#autopurge.purgeInterval=1
## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpHost=0.0.0.0
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
- 启动zookeeper服务
cs
# 启动, 如果不指定配置文件, 默认找conf/zoo.cfg
bin/zkServer.sh start
# 指定配置文件启动
bin/zkServer.sh start conf/my_zoo.cfg
#查看zookeeper状态
bin/zkServer.sh status
# 关闭服务
bin/zkServer.sh stop
# 重启服务
bin/zkServer.sh restart
- 客户端连接
cs
# 连接本地zookeeper服务
bin/zkCli.sh
# 连接远程的zookeeper server
bin/zkCli.sh -server ip:port
- 查看基本命令
5.1 客户端输入help
5.2 官网查看客户端命令
ZooKeeper: Because Coordinating Distributed Systems is a Zoo
- 增删改查等操作
cs
# 查看/下子节点
ls /
# 新增
create /kk a1
# 修改
set /kk a2
# 查看/kk
get /kk
# 删除
delete /kk
集群版搭建
搭建1Leader和2Follower集群, 这里简化版, 在一台服务器上搭建
- 从zoo_sample.cfg复制三份配置文件, 以及创建对应的data和log目录
cs
# config1配置
# 客户端连接端口
clientPort=2181
# 三个实例的ip; 端口1: Leader和Follower交换信息端口; 端口2: 选举通信接口
server.1=192.168.6.128:12888:13888
server.2=192.168.6.128:12889:13889
server.3=192.168.6.128:12890:13890
# 存数据路径
dataDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo1/data
# 存日志路径
dataLogDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo1/log
# config2配置
clientPort=2182
server.1=192.168.6.128:12888:13888
server.2=192.168.6.128:12889:13889
server.3=192.168.6.128:12890:13890
dataDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo2/data
dataLogDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo2/log
# config3配置
clientPort=2183
server.1=192.168.6.128:12888:13888
server.2=192.168.6.128:12889:13889
server.3=192.168.6.128:12890:13890
dataDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo3/data
dataLogDir=/home/kk/local/apache-zookeeper-3.8.3-bin/zoo3/log
- 去三个data目录创建myid文件, 对应上面config里面的server.1
- 分别启动三个zookeeper实例
cs
bin/zkServer.sh start /home/kk/local/apache-zookeeper-3.8.3-bin/zoo1/conf/zoo.cfg
bin/zkServer.sh start /home/kk/local/apache-zookeeper-3.8.3-bin/zoo2/conf/zoo.cfg
bin/zkServer.sh start /home/kk/local/apache-zookeeper-3.8.3-bin/zoo3/conf/zoo.cfg
- 检查节点状态
cs
bin/zkServer.sh status /home/kk/local/apache-zookeeper-3.8.3-bin/zoo1/conf/zoo.cfg
基本语法使用
|--------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------|
| 命令基本语法 | 功能描述 |
| help | 显示所有操作命令 |
| ls [-s] [-w] [-R] path | 使用 ls 命令来查看当前 znode 的子节点 [可监听] -w: 监听子节点变化 -s: 节点状态信息(时间戳、版本号、数据大小等) -R: 表示递归的获取 |
| create [-s] [-e] [-c] [-t ttl] path [data] [acl] | 创建节点 -s : 创建有序节点。 -e : 创建临时节点。 -c : 创建一个容器节点。 t ttl] : 创建一个TTL节点, -t 时间(单位毫秒)。 data:节点的数据,可选,如果不使用时,节点数据就为null。 acl:访问控制 |
| get [-s] [-w] path | 获取节点数据信息 -s: 节点状态信息(时间戳、版本号、数据大小等) -w: 监听节点变化 |
| set [-s] [-v version] path data | 设置节点数据 -s:表示节点为顺序节点 -v: 指定版本号 |
| getAcl [-s] path | 获取节点的访问控制信息 -s: 节点状态信息(时间戳、版本号、数据大小等) |
| setAcl [-s] [-v version] [-R] path acl | 设置节点的访问控制列表 -s:节点状态信息(时间戳、版本号、数据大小等) -v:指定版本号 -R:递归的设置 |
| stat [-w] path | 查看节点状态信息 |
| delete [-v version] path | 删除某一节点,只能删除无子节点的节点。 -v: 表示节点版本号 |
| deleteall path | 递归的删除某一节点及其子节点 |
| setquota -n|-b val path | 对节点增加限制 n:表示子节点的最大个数 b:数据值的最大长度,-1表示无限制 |
可视化客户端
- Zookeeper图形化工具:ZooInspector
- Zookeeper图形化工具:开源的prettyZoo(推荐)
- Zookeeper图形化工具:收费的ZooKeeperAssistant
这里使用prettyZoo, 连接远程zookeeper
数据结构
zookeeper是 文件系统 + key value 数据结构
- 文件系统的树形结构便于表达数据之间的层次关系
- 文件系统的树形结构便于为不同的应用分配独立的命名空间( namespace )
ZooKeeper的层次模型称作Data Tree,Data Tree的每个节点叫作Znode。不同于文件系统,每个节点都可以保存数据,每一个 ZNode 默认能够存储 1MB 的数据,每个 ZNode 都可以通过其路径唯一标识,每个节点都有一个版本(version),版本从0开始计数, 可用来CAS实现乐观锁。
节点分类
zookeeper存在几种不同的节点类型,他们具有不同的生命周期:
|------------------------|-----------------------------------------------------------------------------------|---------------------------------|
| 类型 | 生命周期 | 创建示例 |
| 持久节点 (persistent node) | 一直存在,一直存储在ZooKeeper 服务器上,即使创建该节点的客户端与服务端的会话关闭了,该节点依然不会被删除 | create /locks |
| 临时节点 (ephemeral node) | 当创建该临时节点的客户端会话因超时或发生异常而关闭时,该节点也相应在 ZooKeeper 服务器上被删除。 | create -e /locks/DBLock |
| 有序节点 (sequential node) | 并不算是一种单独种类的节点,而是在之前提到的持久节点和临时节点特性的基础上,增加了一个节点有序的性质。在我们创建有序节点的时候会自动使用一个单调递增的数字作为后缀 | create -e -s /jobs/job (临时有序节点) |
| 容器节点 (container node) | 当一个容器节点的最后一个子节点被删除后,容器节点也会被删除 | create -c /work |
| TTL节点 (ttl node) | 当一个TTL节点在 TTL 内没有被修改并且没有子节点,会被删除。注意:默认此功能不开启,需要修改配置文件extendedTypesEnabled=true | create -t 3000 /ttl_node |
1. 持久节点
客户端与服务端会话关闭, 或服务端重启, 节点依然存在
2. 临时节点
client宕机或者client在指定的timeout时间内没有给ZooKeeper集群发消息,节点就会消失
创建方式: -e
cs
# 创建临时节点
create -e /kk-tmp a3
3. 有序节点
可以和持久节点或临时节点组合
- 持久有序节点
- 临时有序节点
创建方式: -s
4. 容器节点
Container容器节点(3.5.3版本新增),当容器中没有任何子节点,该容器节点会被zk定期删除(定时任务默认60s 检查一次)。 和持久节点的区别是 ZK 服务端启动后,会有一个单独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点。可以用于 leader 或者锁的场景中。
创建方式: -c
cs
# 创建容器节点
create -c /kk-contain aa
5. TTL节点
带过期时间节点, 当一个TTL节点在 TTL 内没有被修改并且没有子节点,会被删除. 默认禁用。
注意:TTL不能用于临时节点
需要在zoo.cfg中配置extendedTypesEnabled参数
cs
# 开启TTL节点
extendedTypesEnabled=true
创建方式: -t
cs
# 创建ttl节点
create -t 10 /kk-ttl
节点状态
cs
# 查看节点状态
[zk: localhost:2181(CONNECTED) 35] ls -s /kk
[k1, k2, k3]
cZxid = 0xcd
ctime = Tue Dec 26 03:36:44 CST 2023
mZxid = 0xcd
mtime = Tue Dec 26 03:36:44 CST 2023
pZxid = 0xd1
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 2
numChildren = 3
参数描述:
- cZxid :Znode创建的事务id。
- ctime:节点创建时的时间戳。
- mZxid :Znode被修改的事务id,即每次对znode的修改都会更新mZxid。对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id),通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的znode。
- pZxid: 表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该ID(注意: 只有子节点列表变更了才会变更pzxid,子节点内容变更不会影响pzxid)
- mtime:节点最新一次更新发生时的时间戳.
- cversion :子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。
- dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题。
- ephemeralOwner:如果该节点为临时节点, ephemeralOwner值表示与该节点绑定的session id。如果不是, ephemeralOwner值为0(持久节点)。在client和server通信之前,首先需要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于closed状态, 此时session结束。
- dataLength : 数据的长度
- numChildren :子节点的数量(只统计直接子节点的数量)
监听机制
watch监听
zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。
支持的事件类型:
- None: 连接建立事件
- NodeCreated: 节点创建
- NodeDeleted: 节点删除
- NodeDataChanged:节点数据变化
- NodeChildrenChanged:子节点列表变化
- DataWatchRemoved:节点监听被移除
- ChildWatchRemoved:子节点监听被移除
命令:
cs
#监听节点数据的变化
get -w /kk
stat -w /kk
#监听子节点增减的变化
ls -w /kk
说明:
|---------|------------------------------------------------------------------------|
| 特性 | 说明 |
| 一次性触发 | watch是一次性的,一旦被触发就会移除,再次使用时需要重新注册 |
| 客户端顺序回调 | watch回调是顺序串行执行的,只有回调后客户端才能看到最新的数据状态。一个watcher回调逻辑不应该太多,以免影响别的watch执行 |
| 轻量级 | WatchEvent是最小的通信单位,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容 |
| 时效性 | watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知; |
永久性watch
- -w: 一次性监听事件
- addWatch: 可持续监听事件
cs
# 持续监听当前节点的修改和删除事件,以及当前节点的子节点的删除和新增事件
addWatch -m PERSISTENT /kk
# - 持久化递归订阅(默认),在PERSISTENT的基础上,增加了子节点修改的事件触发,以及子节点的子节点的数据变化都会触发相关事件
addWatch -m PERSISTENT_RECURSIVE /kk
应用场景
1. 实现分布式锁
多个客户端同时创建临时节点, 只会有一个节点成功. 如果客户端1创建成功, 执行完业务流程后, 释放锁, 也就是删除临时节点, 其他客户端监听该节点删除, 重新竞争锁. 这里用临时节点, 避免客户端宕机情况产生死锁.
cs
# 客户端1
create -c /kk-lock 1
# 客户端2
create -c /kk-lock 1
# 假如客户端1竞争成功, 客户端2创建失败, 客户端2监听该节点
get -w /kk-lock
# 客户端1执行完业务流程后, 释放锁, 删除节点
delete /kk-lock
# 客户端2监听到节点有变动, 重新竞争锁, 创建节点
create -c /kk-lock 1
2. 乐观锁更新数据
更新时带上版本号, CAS更新
cs
# 创建节点
create /kt aa
# 查看节点状态(版本号, 创建时间等)
ls -s /kt
# 带版本号更新, 如果版本不匹配则更新失败
set -v 0 /kt bb
应用场景总结
- 注册中心
- 数据发布/订阅(常用于实现配置中心)
- 负载均衡
- 命名服务
- 分布式协调/通知
- 集群管理
- Master选举
- 分布式锁
- 分布式队列
Zookeeper集群架构
角色
- Leader-领导者
事务请求(写操作)的唯一调度者和处理者,保证集群事务处理的顺序性
- Follower-跟随者
处理客户端非事务(读操作)请求(可以直接响应),转发事务请求给Leader;参与集群Leader选举投票
- Observer-观察者
处理客户端非事务(读操作)请求(可以直接响应),转发事务请求给Leader;不参与选举
Observer应用:
- 提升集群的读性能
- 跨数据中心部署
集群架构
- leader处理读请求和写请求
- follower处理读请求, 把写请求转发到leader
Zookeeper数据一致性保证:
- 全局可线性化(Linearizable )写入∶先到达leader的写请求会先处理,leader决定写请求的执行顺序。
- 客户端FIFO顺序∶来自给定客户端的请求按照发送顺序执行。
选举机制
选举时机
zookeeper集群启动时, 或者leader宕机时, 会触发选举. 老的leader服务恢复时, 会以follower身份加入集群.
选举源码
java
/**
* Check if a pair (server id, zxid) succeeds our
* current vote.
*
*/
protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
LOG.debug(
"id: {}, proposed id: {}, zxid: 0x{}, proposed zxid: 0x{}",
newId,
curId,
Long.toHexString(newZxid),
Long.toHexString(curZxid));
if (self.getQuorumVerifier().getWeight(newId) == 0) {
return false;
}
/*
* We return true if one of the following three cases hold:
* 1- New epoch is higher
* 2- New epoch is the same as current epoch, but new zxid is higher
* 3- New epoch is the same as current epoch, new zxid is the same
* as current zxid, but server id is higher.
*/
return ((newEpoch > curEpoch)
|| ((newEpoch == curEpoch)
&& ((newZxid > curZxid)
|| ((newZxid == curZxid)
&& (newId > curId)))));
}
选举规则
先介绍几个重要参数:
- Epoch: 选举轮次
- Zxid: 最后一次提交的事务id. 可用来判断事务执行先后
- myId: 自定义的服务id
投票规则
-
比较Epoch, epoch大的成为Leader
-
若Epoch一样, 比较Zxid, Zxid大(数据更完整)的成为Leader
-
若Zxid一样, 比较myId, myId大的成为Leader
-
若投票未超过半数, 继续下一轮选举, 下一轮票数少的会改投票数获胜的
zxid结构
- zxid是一个64位的整数,由高32位的epoch和低32位的counter组成。
- epoch表示ZooKeeper服务器的逻辑时期(logical epoch),它是一个相对时间的概念,用于区分不同的Leader选举周期。
- counter是一个在每个时期(epoch)内递增的计数器,用于标识事务的顺序。
四字命令使用
1. 安装nc
cs
yum install nc
2. 开启nc
方式一: zoo.cfg 文件里加入配置项
cs
#开启四字命令
4lw.commands.whitelist=*
方式二: 启动脚本zkServer.sh中新增指令
cs
#添加JVM环境变量-Dzookeeper.4lw.commands.whitelist=*
ZOOMAIN="-Dzookeeper.4lw.commands.whitelist=* ${ZOOMAIN}"
3. 验证
stat 命令用于查看 zk 的状态信息
cs
echo stat | nc 192.168.6.128 2182
4. 其他用法
|------|-----------------------------------------------------------------------------|
| 四字命令 | 功能描述 |
| conf | 3.3.0版本引入的。打印出服务相关配置的详细信息。 |
| cons | 3.3.0版本引入的。列出所有连接到这台服务器的客户端全部连接/会话详细信息。包括"接受/发送"的包数量、会话id、操作延迟、最后的操作执行等等信息。 |
| crst | 3.3.0版本引入的。重置所有连接的连接和会话统计信息。 |
| dump | 列出那些比较重要的会话和临时节点。这个命令只能在leader节点上有用。 |
| envi | 打印出服务环境的详细信息。 |
| reqs | 列出未经处理的请求 |
| ruok | 测试服务是否处于正确状态。如果确实如此,那么服务返回"imok",否则不做任何相应。 |
| stat | 输出关于性能和连接的客户端的列表。 |
| srst | 重置服务器的统计。 |
| srvr | 3.3.0版本引入的。列出连接服务器的详细信息 |
| wchs | 3.3.0版本引入的。列出服务器watch的详细信息。 |
| wchc | 3.3.0版本引入的。通过session列出服务器watch的详细信息,它的输出是一个与watch相关的会话的列表。 |
| wchp | 3.3.0版本引入的。通过路径列出服务器watch的详细信息。它输出一个与session相关的路径。 |
| mntr | 3.4.0版本引入的。输出可用于检测集群健康状态的变量列表 |
官方文档: https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_4lw