前言
本文基于 ZooKeeper 3.8.0 版本。
ZooKeeper集群搭建
准备四台服务器,IP地址分别为10.211.55.6、10.211.55.7、10.211.55.8、10.211.55.9
下载并解压 ZooKeeper 文件,四台服务器进入 data 目录分别创建一个 myid 文件,文件内容分别为1、2、3、4。然后分别进入 conf 目录,复制 zoo_sample.cfg文件到 zoo.cfg 文件。
每台服务器的 zoo.cfg 文件分别添加如下:
server.1=10.211.55.7:2188:3188
server.2=10.211.55.6:2188:3188
server.3=10.211.55.8:2188:3188
server.4=10.211.55.9:2188:3188:observer
其中作为 Observer 角色的 10.211.55.9 服务器的 zoo.cfg 文件额外添加如下:
peerType=observer
启动 ZooKeeper,命令如下:
./bin/zkServer.sh start
查看 ZooKeeper 的角色,命令如下:
./bin/zkServer.sh status
关闭 ZooKeeper,命令如下:
./bin/zkServer.sh stop
可视化工具可以选择 PrettyZoo
下载地址
mac版本安装失败解决方案
分布式架构
从集中式到分布式
集中式系统存在单点问题。一旦一台大型主机出现故障,导致整个系统不可用。
随着业务规模的扩大,集中式为代表的大型机的性能可能会出现瓶颈。
而大型机只能通过堆硬件的方式来垂直拓展,成本高而收效低。
而分布式相较于集中式具备高性能、高可用、可拓展三个重要的特性。
集中式的特点
所谓的集中式系统是指一台或者多台主计算机组成中心节点,数据集中存储于这个中心节点,并且整个系统的所有业务单元都集中部署在这个中心节点上,系统的所有功能均由其集中处理。简言之,数据的存储与控制处理完全由主机来完成。
集中式的最大的特点就是部署结构简单。由于集中式系统往往基于底层性能强大的大型主机,所以无需考虑对服务进行多个节点的部署,也就不用考虑多个节点之间的分布式协作问题。
分布式的特点
在《分布式系统概念与设计》一书中,对分布式系统定义为:一个硬件或者软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。
分布式系统具备如下几个特点:
- 分布式:分布式系统中的多台计算机都会在空间上随意分布,同时,机器的分布情况也会随时变动。
- 对等性:分布式系统中的计算机没有主从之分,各个计算机节点都是对等的。为了对外提供高可用的服务,可以对数据和服务进行副本处理。数据副本是指在不同的节点上持久化同一份数据,当一个节点存储的数据丢失时,可以从副本上读取该数据。服务副本是指多个节点提供相同的服务,每个节点都有能力接收来自外部的请求并进行处理。
- 并发性:分布式系统中的多个节点可能会并发进行一些操作。
- 缺乏全局时钟:分布式系统缺乏一个全局的时钟序列控制。
- 故障总是会发生:分布式系统的各个计算机,都有可能发生任何形式的故障。一个被大量工程实践所检验过的黄金定理是:任何在设计阶段考虑到的异常情况,一定会在系统实际运行中发生,并且在系统实际运行过程中还会遇到很多在设计时未能考虑到的异常故障。
分布式环境的问题
在分布式环境下,可能会遇到网络、存储方面的问题:
- 网络故障:分布式系统需要在各个节点之间进行网络通信。由于网络本身的不可靠性,因此每次网络通信可能会面临网络不可用的问题。此外,也有可能遇到网络延迟、丢包、乱序等问题。如果发生局部网络异常导致只有部分节点之间可以正常通信,从而出现网络分区问题。极端情况下这些局部小集群会独立完成原本需要整个分布式系统才能完成的功能,包括对数据的事务处理,这会给分布式一致性带来非常大的挑战。
- 存储故障:分布式系统的某个节点出现单点问题,比如宕机、磁盘损坏等。
CAP理论
一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)、分区容错性(P:Partition tolerance),最多只能同时满足其中两项。
一致性:数据在多个副本之间是否能够保持一致的特性。对于一个将数据副本分布在不同分布式节点上的系统来说,如果对第一个节点的数据进行更新并且更新成功后,其它节点上的数据也要得到相应的更新。如果能够做到对一个数据项的更新操作执行成功,所有的用户都可以读取到最新值,那么这样的系统也被认为是具有强一致性(或者严格的一致性)。
可用性:系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。“有限的时间”是指合理的响应时间,不同场景下响应时间的期望值有所不同。“返回结果”是指系统在完成用户请求的处理后,返回一个正常的响应结果,即成功或者失败,不能是一个让用户感到困惑的结果。
分区容错性:分布式系统在遇到网络分区时,仍然需要保证对外提供满足一致性和可用性的服务,除非是整个网络都发生了故障。“网络分区”是指分布式系统中,不同的节点分布在不同的子网络,由于一些特殊原因导致这些子网络之间出现网络不连通的情况,从而使整个系统的网络环境被切分成了若干孤立的区域。
BASE理论
BASE 是 Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致性)的简写。BASE 是对 CAP 一致性和可用性权衡的结果,面向的是大型高可用可拓展的分布式系统,核心思想是即使无法做到强一致性,但是每个应用可以根据自身的业务特点,采用适当的方式使系统达到最终一致性。也就是说牺牲强一致性来获得可用性,并且允许数据在一段时间内是不一致的,但是最终达到一致性状态。
基本可用:分布式系统出现不可预知故障时,允许损失部分可用性,但不等价于系统不可用。
- 响应时间上的损失:正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但是由于故障,查询结果的响应时间增加到了1~2秒。
- 功能上的损失:正常情况下,在一个电子商务网站上进行购物,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促高峰时,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
软状态:也称为弱状态,是指允许系统中的数据存在中间状态,并认为这种中间状态的存在不会影响系统的整体可用性,即允许在不同节点的数据副本之间进行数据同步的过程存在延时。
最终一致性:系统中的所有数据副本,在经过一段时间的同步后,最终能够达到一个一致性的状态。
一致性协议
2PC
即 Two Phrase Commit,二阶段提交。
它是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性和一致性而设计的一种算法。通常二阶段提交协议也被认为是一种一致性协议,用来保证分布式系统的数据一致性。目前,绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事务处理的。
阶段一、执行事务/准备阶段
1、协调者向所有参与者发送 Prepare 请求,询问是否可以执行事务提交操作,然后等待各参与者的响应。
2、各参与者执行事务操作,并将 Undo、Redo 信息记录到事务日志中。
3、各参与者向协调者反馈事务询问的响应(如果参与者成功执行了事务操作则反馈 Yes,否则反馈 No)。
阶段二、提交事务/提交阶段
如果协调者收到的反馈都是 Yes,则提交事务。如果有任意一个参与者反馈的是 No 或者在等待超时后协调者没有接收到所有参与者的反馈,则中断事务。
提交事务
1、协调者向所有参与者发送 Commit 请求。
2、参与者接收到 Commit 请求后,提交事务,并且在提交完成后释放占用的事务资源。
3、参与者在完成事务提交后,向协调者发送 Ack 消息。
4、协调者接收到所有参与者的 Ack 消息后,完成事务。
中断事务
1、协调者向所有参与者发送 Rollback 请求。
2、参与者接收到 Rollback 请求后,使用 Undo 信息进行事务回滚,并且在回滚完成后释放占用的事务资源。
3、参与者在完成事务回滚后,向协调者发送 Ack 消息。
4、协调者接收到所有参与者的 Ack 消息后,中断事务。
优点:
- 原理简单
- 实现方便
缺点:
- 同步阻塞:协调者在等待其它参与者响应的过程中,参与者处于阻塞状态,无法进行其它操作。
- 单点问题:如果协调者出现问题,整个二阶段提交流程无法流转。
- 数据不一致:在阶段二中,如果协调者发送完 Commit 请求后,发生了局部网络异常,导致只有部分参与者接收到了请求并进行事务提交,其它没有收到的参与者无法进行事务提交,最终导致数据不一致。
- 太过保守:缺乏完善的容错机制,任意一个节点的失败都会导致整个事务的失败。在阶段一时,如果有参与者出现故障,导致协调者只能依靠自身的超时机制判断是否需要中断事务。
3PC
即 Three-Phrase Commit,三阶段提交。
它是二阶段提交的改进型,将二阶段提交的 “准备阶段” 一分为二,形成了由 canCommit、preCommit、doCommit 三个阶段组成的事务处理协议。相较于二阶段提交只有协调者有超时机制,三阶段提交对参与者增加了超时机制。
阶段一、canCommit
1、协调者向所有的参与者发送 canCommit 请求,询问是否可以执行事务提交操作,并且等待各参与者的响应。
2、参与者如果认为自己可以顺利执行事务,则反馈 YES 响应并进入预备状态;否则反馈 NO 响应。
阶段二、preCommit
如果协调者从所有的参与者获得的反馈都是 YES 响应,就会执行事务预提交。
如果有任何一个参与者反馈的是 NO 响应,或者等待超时后协调者无法接收到所有参与者的反馈响应,则中断事务。
事务预提交
1、协调者向所有参与者发送 preCommit 请求。
2、参与者执行事务操作,并将 Undo、Redo 信息记录到事务日志中。
3、如果参与者成功执行了事务操作,则向协调者反馈 ACK 响应;否则反馈 NO 响应。
中断事务
1、协调者向所有参与者发送 abort 请求。
2、参与者中断事务。
阶段三、doCommit
如果协调者从所有的参与者获得的反馈都是 ACK 响应,就会执行事务提交。
如果有任何一个参与者反馈的是 NO 响应,或者等待超时后协调者无法接收到所有参与者的反馈响应,则中断事务。
事务提交
1、协调者向所有参与者发送 doCommit 请求。
2、参与者提交事务,并且在提交完成之后释放在整个事务执行过程中占用的事务资源,完成事务提交之后向协调者发送 ACK 响应。
3、协调者接收到所有参与者反馈的 ACK 响应后完成事务。
中断事务
1、协调者向所有参与者发送 abort 请求。
2、参与者利用阶段二中记录的 Undo 信息执行事务回滚,并且在回滚完成后释放在整个事务执行期间占用的事务资源。在完成事务回滚后,向协调者发送 ACK 响应。
3、协调者接收到所有参与者反馈的 ACK 响应后中断事务。
优点:
- 降低了参与者的阻塞范围:阶段二的参与者在等待指定时间后仍未接收到协调者发送的 preCommit 请求则取消阻塞,可以做其它事;阶段三的参与者在等待指定时间后仍未接收到协调者发送的 doCommit 请求则提交事务。
- 对参与者增加超时机制,即使协调者出现单点故障后,整个三阶段提交流程继续流转。
缺点:
- 数据不一致:在阶段三时,如果发生网络分区问题,协调者所在的节点无法与参与者进行正常的通信,参与者由于超时机制最终会提交事务,这会带来数据不一致的问题。
ZooKeeper架构
介绍
ZooKeeper 是一个开源的分布式协调服务,是 Google Chubby 的开源实现。ZooKeeper 的设计目标是将那些复杂并且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
分布式协调服务:分布式场景下存在多个节点,每个节点都可以提出请求,经过协调服务的协调,使各节点达成一致,从而确定最终的请求。用于解决分布式环境下的多进程的同步控制,使其顺序访问某个临界区,防止产生脏数据。
ZooKeeper 是一个典型的分布式数据一致性的解决方案,可以保证如下的分布式一致性特性:
- 顺序一致性:从同一个客户端发起的事务请求,最终会严格按照其发起顺序应用到 ZooKeeper 中。
- 原子性:所有事务请求的处理结果在整个集群中的所有机器的应用情况都是一致的,要么全部应用,要么全部没有应用。
- 单一视图:无论客户端连接的是哪个 ZooKeeper 服务器,其看到的服务端数据模型都是一致的。
- 可靠性:一旦服务端成功应用了一个事务,并且完成了对客户端的响应,那么该事务所引起的服务端状态变更会一直保留下来,除非有另一个事务又对其进行了修改。
- 实时性:可以保证一定的时间段内,客户端最终一定能够从服务端读取到最新的数据状态。
通过 sync 方法,也提供了强一致性的保证。
ZAB协议
ZooKeeper 使用 ZAB(ZooKeeper Atomic Broadcast,即原子消息广播协议)作为分布式数据一致性协议。
ZAB 协议是为 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各副本之间的数据一致性。
具体的,ZooKeeper 使用单一的主进程(即 Leader 服务器)来接收并处理客户端的所有事务请求,然后将服务器数据的状态变更以事务 Proposal 的形式广播到所有的副本进程。另一方面,考虑到分布式环境中,顺序执行的一些状态变更其前后存在一定的依赖关系,有些状态变更必须依赖于比它更早生成的那些状态变更。这样的依赖关系也对 ZAB 协议提出了一个要求:ZAB 协议必须能够保证一个全局的变更序列被顺序应用,也就是说,需要保证如果一个状态变更已经被处理了,那么所有依赖的状态变更都应该被提前处理掉了。最后考虑到主进程在任何时候都有可能出现崩溃退出或者重启现象,因此 ZAB 协议还需要做到在当前主进程出现上述异常情况的时候,依旧能够正常工作。
ZAB 协议包括两种模式 - 崩溃恢复和消息广播。
崩溃恢复
当整个服务框架在启动过程中,或者当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况,或者集群中已经不存在过半的 Follower 服务器与 Leader 服务器的数据状态保持一致时,ZAB 协议就会进入恢复模式并选出新的 Leader 服务器,后续如果集群中已经有过半的 Follower 服务器与 Leader 服务器的数据状态保持一致之后,ZAB 协议就会退出恢复模式并进入消息广播模式。
对于新加入集群的服务器,如果此时集群中已经存在一个 Leader 服务器在进行消息广播,那么它就会进入恢复模式(找到 Leader 服务器进行数据同步,然后一起参与到消息广播流程中)。
消息广播
消息广播的过程使用的是一个原子广播协议,类似于二阶段提交。对于客户端的事务请求,Leader 服务器会为其生成对应的事务 Proposal,并将其发送到集群中的所有 Follower 服务器,然后收集各自的选票,最后进行事务提交。
与 2PC 不同的是,ZAB 的消息广播移除了中断逻辑,这意味着可以在过半的 Follower 服务器反馈 Ack 之后就可以提交事务了。这种方式在 Leader 服务器崩溃退出时会带来数据不一致的问题,但是可以使用 ZAB 的崩溃恢复模式来解决这个问题。
在整个消息广播过程中,Leader 服务器会为每个事务请求生成对应的 Proposal,然后分配一个全局单调递增的事务ID(即 ZXID)。此外,Leader 服务器会为每一个 Follower 服务器分配一个单独的队列,然后将需要广播的 Proposal 依次放入到这些队列中,并且根据 FIFO 的策略进行消息发送。Follower 服务器接收到这个事务 Proposal 后,使用事务日志的形式写入到本地磁盘中,写入成功后反馈 Ack 消息给 Leader 服务器。当 Leader 服务器接收到超过半数的 Follower 服务器的 Ack 响应后,就会广播一个 Commit 消息给所有的 Follower 服务器通知其提交事务,同时 Leader 服务器自身也会完成对事务的提交。Follower 服务器接收到 Commit 消息后,进行提交事务。
ZAB 与 Paxos 的相同点、不同点
相同点:
1、两者都存在一个类似于 Leader 进程的角色,由其负责协调多个 Follower 进程的运行。
2、Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提案进行提交。
3、ZAB 协议中,每个 Proposal 中都包含一个 epoch 值来代表当前的 Leader 周期,Paxos 中名字为 Ballot。
不同点:
ZAB 用来构建高可用的分布式数据主备系统(Zookeeper),Paxos 是用来构建分布式一致性状态机系统。
数据模型
ZooKeeper 使用 ZNode 结构存储数据。
ZNode 是 ZooKeeper 数据的最小单元,大小应该小于 1 MB。除了可以存储数据,还可以挂载子节点。
多个ZNode节点按照层次化结构进行组织,形成了树。
The ZooKeeper client and the server implementations have sanity checks to ensure that znodes have less than 1M of data, but the data should be much less than that on average.
节点类型
ZooKeeper 支持的节点类型有:持久节点、临时节点、顺序节点、容器节点(3.5.3 版本开始支持)、TTL(3.5.3 版本开始支持)五种基本类型。
- 持久节点:节点一旦被创建就一直存储在 ZooKeeper 服务器上。
- 临时节点:生命周期与客户端会话绑定在一起。如果客户端会话失效,该节点会被自动清理。临时节点不允许有子节点。
- 顺序节点:具有顺序性。会在节点路径的末尾添加10位单调递增的数字。数字对于父节点来说是唯一的。
- 容器节点:当容器中的所有子节点被删除后,该容器会在将来的某一时刻被删除。
- TTL节点:对于持久节点、持久顺序节点,如果在指定时间没有被修改并且没有子节点,该节点会在将来的某一时刻被删除。
五种基本类型的节点可以组成多种类型的节点:
- PERSISTENT
- PERSISTENT_SEQUENTIAL
- EPHEMERAL
- EPHEMERAL_SEQUENTIAL
- CONTAINER
- PERSISTENT_WITH_TTL
- PERSISTENT_SEQUENTIAL_WITH_TTL
服务器角色
ZooKeeper 服务器角色分为:Leader、Follower、Observer
-
Leader
事务请求的唯一调度者和处理者。保证集群事务处理的顺序性。
集群内部各服务器的调度者。
-
Follower
处理客户端的非事务请求,转发事务请求给 Leader。
参与 Leader 选举的投票。
参与事务请求 Proposal 的投票。
-
Observer
处理客户端的非事务请求,转发事务请求给 Leader。
监听器机制
在 ZooKeeper 3.8.0 版本中 addWatch 命令支持添加一个持久监听器、持久递归监听器,get、stat、ls 命令的 -m 选项支持添加一个一次性监听器,removewatches 命令支持删除一个节点的监听器。
addWatch 命令
添加一个监听器(默认是持久递归监听器)。
语法格式:addWatch [-m mode] path
- -m:指定监听器的模式。支持 PERSISTENT、PERSISTENT_RECURSIVE,默认是 PERSISTENT_RECURSIVE
[zk: localhost:2181(CONNECTED) 30] addWatch -m PERSISTENT_RECURSIVE /test
[zk: localhost:2181(CONNECTED) 31] set /test/child1 2
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/test/child1
[zk: localhost:2181(CONNECTED) 32] set /test 123
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/test
get 命令
获取指定节点的数据内容。
语法格式:get [-s] [-w] path
- -s:额外展示stat信息
- -w:额外添加一个一次性的监听器(监听节点的数据内容变化)
[zk: localhost:2181(CONNECTED) 6] get /test
123
[zk: localhost:2181(CONNECTED) 7] get -s /test
123
cZxid = 0x4
ctime = Wed Dec 21 17:03:08 CST 2022
mZxid = 0x4
mtime = Wed Dec 21 17:03:08 CST 2022
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
[zk: localhost:2181(CONNECTED) 8] get -w /test
123
[zk: localhost:2181(CONNECTED) 9] set /test 1234
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/test
stat 命令
获取指定节点的stat信息。
语法格式:stat [-w] path
- -w:额外添加一个一次性的监听器(监听节点的数据内容变化)
[zk: localhost:2181(CONNECTED) 12] stat /test
cZxid = 0x4
ctime = Wed Dec 21 17:03:08 CST 2022
mZxid = 0x6
mtime = Wed Dec 21 17:17:52 CST 2022
pZxid = 0x4
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 13] stat -w /test
cZxid = 0x4
ctime = Wed Dec 21 17:03:08 CST 2022
mZxid = 0x6
mtime = Wed Dec 21 17:17:52 CST 2022
pZxid = 0x4
cversion = 0
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 14] set /test 12345
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/test
ls 命令
列举出指定节点下的所有子节点。
语法格式:ls [-s] [-w] [-R] path
- -s:额外展示stat信息
- -R:递归展示所有子节点
- -w:额外添加一个一次性的监听器(监听节点的创建/删除、子节点的创建/删除)
[zk: localhost:2181(CONNECTED) 18] ls /test
[child1, child2]
[zk: localhost:2181(CONNECTED) 19] ls -s /test
[child1, child2]
cZxid = 0x4
ctime = Wed Dec 21 17:03:08 CST 2022
mZxid = 0x8
mtime = Wed Dec 21 17:22:12 CST 2022
pZxid = 0xa
cversion = 2
dataVersion = 4
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 2
[zk: localhost:2181(CONNECTED) 20] ls -R /test
/test
/test/child1
/test/child2
[zk: localhost:2181(CONNECTED) 21] ls -w /test
[child1, child2]
[zk: localhost:2181(CONNECTED) 22] delete /test/child2
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/test
removewatches 命令
删除在指定路径上设置的监听器。
语法格式:removewatches path [-c|-d|-a] [-l]
[zk: localhost:2181(CONNECTED) 33] removewatches /test
WATCHER::
WatchedEvent state:SyncConnected type:PersistentWatchRemoved path:/test
printwatches 命令
触发监听器后是否打印监听器事件信息。
语法格式:printwatches on|off
[zk: 10.211.55.6:2181(CONNECTED) 12] printwatches
printwatches is on
[zk: 10.211.55.6:2181(CONNECTED) 13] printwatches off
[zk: 10.211.55.6:2181(CONNECTED) 14] printwatches
printwatches is off
[zk: 10.211.55.6:2181(CONNECTED) 15] printwatches on
[zk: 10.211.55.6:2181(CONNECTED) 16] printwatches
printwatches is on
服务器信息的监控
四字命令
在 ZooKeeper 的 zoo.cfg 文件中添加如下一行开启四字命令:
4lw.commands.whitelist=*
ZooKeeper 支持如下四字命令:ZooKeeper 四字命令参考。echo <command> | ncat <ip> <port>
- ruok:测试服务端是否运行正常
- stat:列举服务端与所有与之连接的客户端的简要信息
- …
Admin Server
可以在浏览器中输入如下获取对应的服务器信息:
http://10.211.55.6:8080/commands/<command>
JMX
在 zkServer.sh 文件找到 “ZOOMAIN” 关键字,然后在"="的右侧追加如下内容,即开启远程 JMX 端口为 5000,同时不需要任何权限:
-Djava.rmi.server.hostname=10.211.55.6
-Dcom.sun.management.jmxremote.port=5000
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
然后将原有的 -Dcom.sun.management.jmxremote.local.only
对应的值改成 false。
ACL权限
ZooKeeper 提供了一套完善的 ACL(Access Control List)权限控制机制来保障数据的安全。ACL 机制分为三个方面 - 权限模式(scheme)、授权对象(id)、权限(permission),通常使用 scheme:id:permission
来标识一个 ACL 信息。
权限模式
IP:以 IP 地址作为权限控制的粒度。比如配置为 “ip:192.168.0.110”,表示权限控制都是针对这个 IP 地址的。也支持按照网段的方式进行配置,比如配置为 “ip:192.168.0.1/24”,表示针对这个 192.168.0.* 这个网段进行权限控制。
Digest:使用 “username:password” 的形式进行权限控制。ZooKeeper 对其进行两次编码处理,分别是 SHA-1 算法加密、BASE64 编码。
DigestAuthenticationProvider.generateDigest("hello:world"); // hello:U3hIgRbJuk9g5+5vQnEfobKs1jA=
World:对所有用户开放,可以看作是一种特殊的 Digest 模式,它只有一个权限标识,即 “world:anyone”。
Super:超级用户可以执行任何操作,也可以看作是一种特殊的 Digest 模式。
授权对象
对于 IP 权限模式,授权对象是一个 IP 地址或者 IP 网段,比如 192.168.0.110 或者 192.168.0.1/24。
对于 Digest 权限模式,授权对象是 “hello:U3hIgRbJuk9g5+5vQnEfobKs1jA=”,即对 "hello:world"进行两次编码处理后的字符串。
对于 World 权限模式,授权对象只有 “anyone”。
对于 Super 权限模式,授权对象与 Digest 权限模式的相同。在 ZooKeeper 服务器启动时添加如下的系统属性来开启 Super 模式:
-Dzookeeper.DigestAuthenticationProvider.superDigest=hello:U3hIgRbJuk9g5+5vQnEfobKs1jA
权限
CREATE(c):节点的创建权限。允许授权对象在该节点下创建子节点。
DELETE(d):节点的删除权限。允许授权对象删除该节点下的子节点。
READ(r):节点的读取权限。允许授权对象读取该节点的数据内容和子节点列表。
WRITE(w):节点的修改权限。允许授权对象修改该节点的数据内容。
ADMIN(a):节点的管理权限。允许授权对象对该节点设置 ACL 权限操作。
addauth 命令
添加一个授权的用户。
语法格式:addauth scheme auth
[zk: localhost:2181(CONNECTED) 21] create -e /acl-demo 123
Created /acl-demo
[zk: localhost:2181(CONNECTED) 22] setAcl /acl-demo digest:hello:U3hIgRbJuk9g5+5vQnEfobKs1jA=:cdrwa
[zk: localhost:2181(CONNECTED) 23] addauth digest hello:world
[zk: localhost:2181(CONNECTED) 24] get /acl-demo
123
[zk: localhost:2181(CONNECTED) 25] getAcl /acl-demo
'digest,'hello:U3hIgRbJuk9g5+5vQnEfobKs1jA=
: cdrwa
setAcl 命令
为节点设置ACL权限。
语法格式:setAcl [-s] [-v version] [-R] path acl
- -s:额外输出Stat信息
- -v version:设置ACL版本号
- -R:对节点下的所有子节点递归设置ACL权限
getAcl 命令
获取指定节点的ACL权限
语法格式:getAcl [-s] path
- -s:额外输出Stat信息
事务日志
ZooKeeper 的 zoo.cfg 配置文件中的 dataDir、dataLogDir 属性分别对应 ZooKeeper 快照文件、事务日志文件的存储位置;如果只有 dataDir,则事务日志文件会选择与快照文件相同的存储位置。
在 ${dataLogDir}/version-2 目录下可以看到多个文件名为"log",其后缀都是十六进制数字,这些文件都是 ZooKeeper 的事务日志文件。这些事务日志文件的大小都是 64MB。事务日志文件的后缀其实是一个事务ID,即 ZXID,并且是写入该事务日志文件的第一条事务记录的 ZXID。使用 ZXID 作为文件的后缀,可以迅速定位到某一条事务操作所在的事务日志。此外,ZXID 本身由两部分组成,高 32 位表示当前 Leader Epoch,低 32 位表示操作序列号,因此第二个优点就是根据高 32 位看出当前的 Leader Epoch。
借助 ZooKeeper 提供的 zkTxnLogToolkit.sh 可以查看事务日志的内容。
11/24/22, 10:51:05 PM CST session 0x1000001dcec0000 cxid 0x235 zxid 0x30000000fd multi check:/controller_epoch(;setData:6/brokers/topics/__consumer_offsets/partitions/40/stateK{"controller_epoch":39,"leader":-1,"version":1,"leader_epoch":35,"isr":[2]}#
数据快照
记录 ZooKeeper 服务器上某一时刻的全量内存数据,将其写入到 ZooKeeper 的 zoo.cfg 配置文件中的 dataDir 属性对应的 ZooKeeper 快照文件中。
在 ${dataDir}/version-2 目录下可以看到多个文件名为"snapshot",其后缀都是十六进制数字,这些文件都是 ZooKeeper 的数据快照文件。快照文件的后缀也是一个事务ID,即 ZXID,标识本次数据快照的开始时刻的服务器最新的 ZXID。借助这个 ZXID,可以在数据恢复阶段来确定数据恢复的起始点。
借助 ZooKeeper 提供的 zkSnapShotToolkit.sh 可以查看数据快照的内容。
/brokers/topics/thing4
cZxid = 0x0000040000010c
ctime = Mon Aug 08 15:48:34 CST 2022
mZxid = 0x0000040000010c
mtime = Mon Aug 08 15:48:34 CST 2022
pZxid = 0x0000040000010d
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 138
Chroot 特性
3.2.0 版本开始,添加了 Chroot 特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在自己的命名空间下。Chroot 特性实现了多客户端在 ZooKeeper 服务端的应用隔离。
new ZooKeeper("10.211.55.6:2181/path", 30000, watcher);
斜杠右边的就是 chroot。
客户端命令
addWatch、get、ls、stat、removewatches、printwatches 命令详见 “监听器机制” 部分。
addauth、setAcl、getAcl 命令详见 “ACL权限” 部分。
create 命令
创建节点(默认是持久节点)。
语法格式:create [-s] [-e] [-c] [-t ttl] path [data] [acl]
- -s:顺序节点
- -e:临时节点
- -c:容器节点
- -t ttl:TTL节点(单位为毫秒)
[zk: localhost:2181(CONNECTED) 3] create /demo
Created /demo
[zk: localhost:2181(CONNECTED) 4] create -s /demo1
Created /demo10000000090
[zk: localhost:2181(CONNECTED) 6] create -c /demo2
Created /demo3
delete 命令
删除节点。
语法格式:delete [-v version] path
- -v:版本号
[zk: localhost:2181(CONNECTED) 15] delete -v -1 /demo2
deleteAll 命令
删除指定路径下的所有节点。
语法格式:deleteall path [-b batch size]
[zk: localhost:2181(CONNECTED) 25] create /demo
Created /demo
[zk: localhost:2181(CONNECTED) 26] create /demo/child1
Created /demo/child1
[zk: localhost:2181(CONNECTED) 27] create /demo/child2
Created /demo/child2
[zk: localhost:2181(CONNECTED) 28] deleteall /demo
set 命令
修改节点内容。
语法格式:set [-s] [-v version] path data
- -s:额外输出Stat信息
- -v:版本号
[zk: localhost:2181(CONNECTED) 29] create /demo
Created /demo
[zk: localhost:2181(CONNECTED) 30] set -v 0 /demo 123
[zk: localhost:2181(CONNECTED) 31] set -v 0 /demo 123
version No is not valid : /demo
[zk: localhost:2181(CONNECTED) 32] set -v 1 /demo 12345
[zk: localhost:2181(CONNECTED) 33] set -s -v 2 /demo 1
cZxid = 0x3c00000155
ctime = Wed Jan 11 15:11:38 CST 2023
mZxid = 0x3c00000159
mtime = Wed Jan 11 15:12:47 CST 2023
pZxid = 0x3c00000155
cversion = 0
dataVersion = 3
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 1
numChildren = 0
getAllChildrenNumber 命令
获取指定路径的所有子节点的数量。
语法格式:getAllChildrenNumber path
[zk: localhost:2181(CONNECTED) 35] getAllChildrenNumber /
413
getEphemerals 命令
获取当前会话中指定路径下的所有临时节点。
语法格式:getEphemerals path
[zk: localhost:2181(CONNECTED) 40] create -e /demo3
Created /demo3
[zk: localhost:2181(CONNECTED) 41] getEphemerals /
[/demo3]
history 命令
查看最近执行过的11个命令。
语法格式:history
[zk: localhost:2181(CONNECTED) 42] history
32 - set -v 1 /demo 12345
33 - set -s -1 /demo 1
34 - set -s -v -1 /demo 1
35 - getAllChildrenNumber /
36 - deleteall /demo2
37 - deleteall /demo
38 - deleteall /demo1
39 - getEphemerals
40 - create -e /demo3
41 - getEphemerals
42 - history
close 命令
关闭客户端/会话。
语法格式:close
connect 命令
连接服务端。
语法格式:connect host:port
quit 命令
离开 CLI 窗口。
语法格式:quit
redo 命令
重新执行历史中指定索引的命令。
语法格式:redo cmdno
[zk: 10.211.55.6:2181(CONNECTED) 10] history
0 - exists /
1 - connect 10.211.55.7:2181
2 - connect 10.211.55.6:2181
3 - create -e /acl-demo 123
4 - setAcl /acl-demo digest:hello:U3hIgRbJuk9g5+5vQnEfobKs1jA=:cdrwa
5 - getAcl /acl-demo
6 - addauth digest hello:world
7 - getAcl /acl-demo
8 - getAcl -s /acl-demo
9 - whoami
10 - history
[zk: 10.211.55.6:2181(CONNECTED) 11] redo 9
Auth scheme: User
ip: 10.211.55.6
digest: hello
sync 命令
在 Leader 与 Follower 之间同步(采用异步的方式)指定节点的数据内容。
语法格式:sync path
version 命令
显示当前ZooKeeper CLI 的版本号。
语法格式:version
whoami 命令
获取当前认证信息。
语法格式:whoami
[zk: 10.211.55.6:2181(CONNECTED) 9] whoami
Auth scheme: User
ip: 10.211.55.6
digest: hello
ZooKeeper原生API
列出一些常用的原生API。
创建会话
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher);
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly);
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd);
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolean canBeReadOnly);
- connectString:服务端的地址,比如 10.211.55.6:2181 或者 10.211.55.6:2181/root(指定根节点)。
- sessionTimeout:会话的超时时间(单位为毫秒)。
- watcher:监听器。
- canBeReadOnly:发生网络分区时,如果集群中没有相互正常通信的过半节点,允许客户端以只读模式访问可达的服务端。
- sessionId:会话id。
- sessionPasswd:会话密码。
e.g.
ZooKeeper zooKeeper = new ZooKeeper("10.211.55.6:2181", 30000, new Watcher() {
@Override
public void process(WatchedEvent event) {
LOGGER.info("Received event : " + event);
}
});
// 会话id和会话密码标识唯一会话,可以用来复用会话
ZooKeeper zooKeeper2 = new ZooKeeper("10.211.55.6:2181", 3000, new Watcher() {
@Override
public void process(WatchedEvent event) {
LOGGER.info("Received event : " + event);
}
}, zooKeeper.getSessionId(), zooKeeper.getSessionPasswd());
创建节点
public String create(String path, byte[] data, List<ACL> acl, CreateMode createMode);
public String create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Stat stat);
public String create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Stat stat, long ttl);
public void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, StringCallback callback, Object ctx);
public void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Create2Callback callback, Object ctx);
public void create(String path, byte[] data, List<ACL> acl, CreateMode createMode, Create2Callback callback, Object ctx, long ttl);
- path:节点路径。
- data:节点的数据内容。
- acl:权限。
- createMode:节点的类型。
- stat:节点的状态信息。
- ttl:节点的生存时间。
- callback:回调函数。
- ctx:上下文对象。
e.g.
zooKeeper.create("/demo", "hello world".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 20L);
删除节点
public void delete(String path, int version);
public void delete(String path, int version, VoidCallback callback, Object ctx);
- path:节点路径。
- version:版本号。可以设置为 -1,表示当前最新版本号。
- callback:回调函数。
- ctx:上下文对象。
查询节点的数据内容
public byte[] getData(final String path, Watcher watcher, Stat stat);
public byte[] getData(String path, boolean watch, Stat stat);
public void getData(final String path, Watcher watcher, DataCallback cb, Object ctx);
public void getData(String path, boolean watch, DataCallback cb, Object ctx);
- path:节点路径。
- watcher:监听器。
- stat:节点的状态信息。
- watch:是否复用监听器。
- cb:回调方法。
- ctx:上下文对象。
查询节点是否存在
Stat exists(String path, Watcher watcher);
Stat exists(String path, boolean watch);
Stat exists(String path, Watcher watcher, StatCallback cb, Object ctx);
Stat exists(String path, boolean watch, StatCallback cb, Object ctx);
- path:节点的路径。
- watcher:监听器。
- watch:是否复用监听器。
- cb:回调函数。
- ctx:上下文对象。
修改节点的数据内容
public Stat setData(String path, byte[] data, int version);
public void setData(String path, byte[] data, int version, StatCallback cb, Object ctx);
- path:节点路径。
- data:节点的数据内容。
- version:版本号。可以设置为 -1,表示当前最新的版本号。
- cb:回调方法。
- ctx:上下文对象。