Zookeeper
一、引言
Zookeeper是一个开源的分布式协调服务,用于管理大型分布式系统中的配置信息、命名服务、分布式锁和分布式应用程序的协同工作等
经典应用场景?
- 命名服务
- 在分布式系统中,不同的节点需要协同工作,但是节点的地址、端口等信息可能会发生变化,此时可以使用Zookeeper作为命名服务,记录节点的地址、端口等信息,并提供动态更新功能
- 配置管理
- 在分布式系统中,对系统的各个组件的配置信息进行统一管理和分发的一种技术
- 数据同步
- 是指在分布式系统中,将多个节点之间的数据保持一致的过程
- leader选举
- 用于在一个分布式系统中选择一个节点作为Leader(领导者)来协调整个系统的工作
- 消息队列
- 可作为分布式消息队列组件的一部分
- 通知系统
- 用作分布式的通知系统,为分布式应用提供可靠的通知和事件服务,通常情况下,Zookeeper的通知系统基于Watch机制实现
二、架构
1.单机架构
安装:支持源文件、docker安装
连接:通过zkCli.sh连接
管理:图形化界面zkui
2.数据模型与基本操作
2.1 分层命名空间
Zookeeper的分层命名空间是指将Zookeeper的节点组织成多层次的树形结构,用于更好地管理和组织节点。每个节点都可以拥有子节点,形成一个树形结构,其中根节点是”/“,下面是结构图

上面介绍了分层命名空间,它将zookeeper的节点按照树形结构组织,那么节点又是什么结构呢?
2.2 节点和临时节点
ZooKeeper 树中的每个节点都称为znode
Znode维护一个stat结构,其中包括版本号和数据变化、acl变化
此外,stat结构还包括时间戳,版本号和时间戳结合起来,允许ZooKeeper验证缓存并协调更新,每当znode的数据更改时,版本号就会增加
例如,每当客户端检索数据时,它还会收到数据的版本号。当客户端执行更新或删除操作时,必须提供正在更改的znode的数据版本。如果提供的版本号与实际数据的版本号不匹配,更新将失败
节点是指存储数据的最小单元。每个节点都可以包含一些元数据和数据,以及一些ACL(访问控制列表)信息,具体来说,每个节点包含的信息如下:
- 路径:节点在Zookeeper中的路径,由斜杠”/“分隔的多个名称组成,例如:”/myapp/config”.
- 数据:节点中存储的数据,可以是任意类型的二进制数据。
- 元数据:包括节点的创建时间、修改时间、版本号等信息。
- ACL(访问控制列表):用于控制节点的访问权限。
节点以字节码形式存储在Zookeeper的内存中,并通过Zookeeper的API进行访问和操作
在Zookeeper中,节点数据是以字节码形式存储的,这意味着节点中存储的数据需要被转换为字节流,并以字节流的形式写入Zookeeper的内存中
使用字节码的好处是可以节省存储空间,并提高数据读取和写入的效率
此外,在Zookeeper中,节点有两种,分别是永久节点与临时节点
- 永久节点:是指创建后一直存在的节点,直到被删除或Zookeeper服务停止
- 临时节点:是指只在客户端会话期间存在的节点,一旦客户端会话结束或失效,临时节点将被自动删除,临时节点的使用可以有效地避免分布式系统中的资源浪费和竞争问题
2.3 节点特性
在Zookeeper中,节点除了基本属性外,还具有一些特性,其中一些特性包括
- 持久节点:持久节点是Zookeeper中最常见的节点类型,它们的生命周期与Zookeeper的生命周期相同。即使客户端与Zookeeper断开连接,持久节点也会一直存在,直到被显式删除
- 临时节点:临时节点的生命周期与客户端的连接相关联。当客户端与Zookeeper断开连接时,临时节点将被删除。这种节点通常用于实现分布式锁等功能
- 有序节点:有序节点是指在创建节点时,Zookeeper会自动为其分配一个全局唯一的序列号,并按照序列号的大小进行排序。有序节点通常用于实现队列等功能
- 可监听节点:Zookeeper的节点支持事件监听,当节点发生变化时,客户端可以接收到通知。这种特性通常用于实现配置管理等功能
- 分层命名空间:Zookeeper的节点支持分层命名空间,可以使用斜杠(/)将节点组织成层次结构。这种特性通常用于实现配置管理和命名服务等功能
此外,介绍两类特殊节点:
容器节点:在 ZooKeeper 中,容器节点是指可以包含其他节点的节点,它们本身不存储任何数据,只是作为其他节点的父节点存在。容器节点也可以称为永久节点,因为它们在创建后一直存在,除非被显式删除。
TTL节点: TTL 节点是指在创建时指定了生存时间的节点。节点在创建后,在指定的时间之后自动删除。这种节点在 ZooKeeper 中也称为短暂节点(ephemeral node)。可以通过向创建 TTL 节点的请求中添加一个超时时间来实现
2.4 时间
在ZooKeeper中,时间是非常重要的概念,它涉及到ZooKeeper的一些关键特性,例如协调更新、数据一致性等,下面是一些相关概念
- Zxid(ZooKeeper Transaction Id)
- Zxid是ZooKeeper事务ID的缩写,用于唯一标识每个事务
- 状态的每次更改都会收到一个zxid 形式的戳记,这公开了对 ZooKeeper 的所有更改的总顺序,每个更改都会有一个唯一的 zxid,如果 zxid1 小于 zxid2,则 zxid1 发生在 zxid2 之前
- 版本号:记录znode是否变化,每次变化版本号+1
- Ticks:Ticks是ZooKeeper中的一个时间概念,它是一个增量时间单位,表示ZooKeeper的时间
- Real time:是指从机器启动时开始的实际时间,通常用于日志记录等目的
这里详细介绍一下zxid,zxid是事务编号,8字节的整型数,即64个比特位,前32位标识epoch,后32位用来计数
zxid的初始值为0,用二进制标识就是 00000000 00000000 00000000 00000000每一次事务请求都会把后面32位的值+1,比如进行了10次事务请求,则zxid变为
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001010
每进行一次leader选举,前32位的值就会+1,并把后面的32位清零,则zxid变为
00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000000
在zookeeper中,zxid有以下表现形式,存储在stat结构中
- czxid:表示当前节点被创建时的事务id
- mzxid:表示当前节点被最后一次更新时的事务id
- pzxid:表示该节点的子节点最后一次被修改时的事务id,只有子节点变化才会影响pxzid
三、高级特性
1.watch
在 ZooKeeper 中,客户端可以使用 watch 机制来监视某个节点的变化情况,当节点的状态发生变化时,ZooKeeper 会将通知发送给所有注册了该节点的 Watcher
具体来说,当一个客户端向 ZooKeeper 注册 Watcher 时,它会在节点上设置一个监视器,并与这个节点建立一个会话。当这个节点的状态发生变化时(例如节点被创建、更新或删除),ZooKeeper 会将通知发送到客户端的会话。客户端会接收到这个通知,然后可以处理节点状态的变化
图解
2.ACL
在ZooKeeper中,ACL(Access Control List)是一种机制,用于控制对znode的访问权限。每个znode都可以关联一个ACL。ACL定义了哪些用户、哪些操作可以访问该znode
ZooKeeper支持以下操作权限:
- READ:读取znode的数据和子节点列表
- WRITE:设置znode的数据
- CREATE:创建子节点
- DELETE:删除znode或子节点
- ADMIN:设置znode的ACL
ACL是一个包含多个元素的列表。每个元素表示一个权限的组合,包括:
- 权限模式(scheme):用于验证身份的机制,例如digest、ip、world、sasl等
- 身份标识(id):用于表示一个用户或角色,例如用户名、IP地址、用户组等
- 权限(permission):对该身份标识具有的权限,例如read、write、create等
每个ACL元素对应一个唯一的编号,ZooKeeper中将其称为ACL ID。当客户端连接到ZooKeeper时,它可以使用auth API来验证自己的身份。如果身份验证成功,客户端会获得一个会话ID,它将与ACL ID关联。在后续的会话中,客户端只能访问与其关联的ACL ID所允许的节点
需要注意的是,设置znode的ACL只能在创建znode时进行,不能修改。如果需要修改znode的ACL,必须删除该znode并重新创建。此外,ZooKeeper的ACL不是强制执行的,而是建议性的,也就是说如果有足够的权限,用户仍然可以访问znode。因此,在使用ACL时,应该根据实际情况进行权衡和规划,以确保ZooKeeper集群的安全和稳定性
以下是常见的ACL方案
- world:world方案是最简单的方案,它可以控制所有用户对znode的访问权限。world方案的规则形式为“scheme:id:perm”,其中scheme为world,id为anyone,perm表示权限(create、delete、read、write、admin)。例如,要授予所有用户读取和写入znode的权限,可以使用规则“world:anyone:rw”。
- auth:auth方案用于控制已通过身份验证的用户对znode的访问权限。它的规则形式为“scheme:id:perm”,其中scheme为auth,id为已经通过身份验证的用户,perm表示权限。例如,要授予名为“user1”的已经通过身份验证的用户读取和写入znode的权限,可以使用规则“auth:user1:rw”。
- digest:digest方案基于用户名和密码来控制访问权限。它的规则形式为“scheme:id:perm”,其中scheme为digest,id为用户名:密码的SHA1摘要值,perm表示权限。例如,要授予用户名为“user1”、密码为“password”的用户读取和写入znode的权限,可以使用规则“digest:user1:3e25960a79dbc69b674cd4ec67a72c62:rw”。
- ip:ip方案基于客户端的IP地址来控制访问权限。它的规则形式为“scheme:ip:perm”,其中scheme为ip,ip为客户端的IP地址,perm表示权限。例如,要授予IP地址为192.168.1.100的客户端读取和写入znode的权限,可以使用规则“ip:192.168.1.100:rw”。
- sasl:sasl方案是一种用于身份验证的开放式标准。它允许客户端使用各种安全协议来进行身份验证,例如Kerberos、OAuth、LDAP等。sasl方案的规则形式为“scheme:id:perm”,其中scheme为sasl,id为已经通过身份验证的用户,perm表示权限。例如,要授予名为“user1”的已经通过SASL身份验证的用户读取和写入znode的权限,可以使用规则“sasl:user1:rw”。
这些ACL方案可以组合使用,例如可以同时使用digest和auth方案来对访问权限进行更精细的控制。需要注意的是,ACL是在创建znode时设置的,一旦设置后就不能修改。因此,在设计应用程序时需要考虑好权限控制策略。
3.Monitoring
在ZooKeeper中,Monitoring通常用于跟踪集群中各个节点的健康状况以及资源使用情况
监控可以帮助管理员快速发现并解决集群中的问题,确保其稳定性和可靠性
ZooKeeper提供了一些内置的监控工具和指标,例如:
- Four Letter Words(四字命令):通过发送包含特定命令的TCP请求来检查和监视ZooKeeper服务器的状态。这些命令包括“stat”、“ruok”、“mntr”等,每个命令都返回特定的信息和指标
- JMX:ZooKeeper使用Java管理扩展(JMX)提供了一组监控和管理API,可用于监视服务器性能和状态,包括znode、客户端连接数、延迟、请求计数等
- Metrics:ZooKeeper还提供了Metrics API,可用于收集和发布关于服务器的指标和数据。可以使用可视化工具(如Grafana)将这些指标呈现出来,以便进行更直观的监控和分析
通过这些监控工具,管理员可以监视集群的运行状况、查找潜在问题并采取必要的措施来解决它们,从而确保ZooKeeper集群的稳定和可靠性
4.序列化和反序列化
在分布式系统中,数据需要在网络上传输,而网络上的传输数据只能是二进制的。为了将数据在网络上传输,需要将其序列化成二进制格式,而在接收端需要将其反序列化为原始数据
ZooKeeper采用的是Jute作为其序列化和反序列化的方式
Jute是ZooKeeper自带的一个二进制编解码库,它提供了一种轻量级的序列化机制,适合于数据结构简单、字段数量不太多的场景。相比于Java自带的序列化方式,Jute的效率更高、数据量更小,同时也更易于跨语言使用
5.快照数据与事务日志
快照数据:记录所有ZNode节点及数据某一时刻的快照,保存在zoo.cfg文件配置项的dataDir目录的的version-2中,格式为snapshot.zxid
事务日志:记录每一次事务操作的记录,保存在dataLogDir[dataDir]目录的的version-2中,格式为log.zxid
那么他们有什么作用呢?事实上,在zookeeper中,快照数据与事务日志共同配合,用于持久化存储和恢复数据,确保数据的可靠性和一致性,是zookeeper数据持久化的实现方式
快照数据:
是ZooKeeper数据的静态拷贝,它记录了ZooKeeper的整个数据树在某个时间点的状态。快照数据包括每个znode的数据内容、版本号、访问控制列表(ACL)和时间戳等信息。当ZooKeeper启动时,它会从快照数据中读取所有znode的状态信息,并使用这些信息重新构建整个数据树。快照数据的作用是提供了一个基准状态,以便ZooKeeper可以在事务日志中记录的操作上进行重放,实现数据的恢复
事务日志:
事务日志是ZooKeeper中的所有更新操作的记录。当客户端执行写操作时,它们将生成一个事务,该事务被追加到事务日志中。每个事务都被分配一个唯一的事务ID,该ID由ZXID(ZooKeeper Transaction ID)表示。每个事务都包括操作类型(例如create、set、delete)以及操作所需的数据。当ZooKeeper重启时,它会读取最新的快照数据,然后重新播放在最后一个快照之后的所有事务,以恢复所有更新操作。因此,事务日志的作用是确保数据在发生故障时不会丢失,并且可以保持最终一致性
总的来说,快照数据和事务日志结合起来,保证了ZooKeeper的可靠性和一致性。快照数据提供了一个基准状态,事务日志记录了所有数据更新操作。当ZooKeeper启动时,它使用快照数据来重建数据树,并重放所有事务日志中记录的操作,以实现数据的恢复和一致性
四、常见问题
Zookeeper是什么?它的作用是什么?
答:Zookeeper是一个开源的分布式协调服务,用于构建分布式应用程序和服务。它提供了一个高可用的、可靠的、有序的节点管理(命名服务)、配置管理、分布式锁、分布式队列等功能。
Zookeeper的节点是什么?节点包括哪些信息?
答:Zookeeper中的节点被称为znode,包括数据内容、版本号、ACL、时间戳等信息。
Zookeeper的watch机制是什么?它的作用是什么?
答:Zookeeper的watch机制是一种事件通知机制,当指定的节点发生变化时,客户端将收到通知。它可以用于实现数据的实时同步、分布式锁、分布式队列等功能。
Zookeeper中的ACL是什么?它的作用是什么?
答:Zookeeper中的ACL(Access Control List)是一种权限控制机制,用于限制对节点的访问。它可以保护节点的安全性,防止恶意访问或篡改数据。
Zookeeper的数据存储方式是什么?它有哪些优势?
答:Zookeeper的数据存储方式是内存数据库,将节点的数据以字节码的形式存储在内存中。它的优势是读写速度快,支持高并发,适合用于构建高性能的分布式应用。
Zookeeper的数据一致性是如何保证的?
答:Zookeeper通过ZAB协议(Zookeeper Atomic Broadcast)来保证数据的一致性,ZAB协议采用了一种主从复制的机制,在主节点上进行数据修改,并将数据变更通过ZAB协议广播给从节点,从而实现数据的一致性。
Zookeeper如何实现分布式锁?
答:Zookeeper实现分布式锁的方法是使用临时顺序节点,每个客户端创建一个临时顺序节点,对应一个锁,当客户端希望获得锁时,它将创建一个新的临时顺序节点,并获取当前所有节点中最小的节点号,如果它是最小的节点号,则认为它获得了锁,否则它将等待前一个节点释放锁后再次尝试。
什么是Zookeeper的ZAB协议?它是如何保证数据一致性的?
Zookeeper是一个开源的分布式协调服务,用于构建分布式应用程序和服务。它提供了一个高可用的、可靠的、有序的节点管理(命名服务)、配置管理、分布式锁、分布式队列等功能
Zookeeper的节点是如何命名的?什么是Zookeeper的命名空间?
Zookeeper的节点使用路径来命名,路径以斜杠(/)分隔,例如/foo/bar。Zookeeper的命名空间是由所有节点路径的集合所组成的层次结构,类似于文件系统中的目录结构。每个节点都被称为znode,并有一个全局唯一的路径名。Zookeeper的命名空间具有层次结构的特点,这使得它非常适合用于分布式应用程序的协调和管理
什么是Zookeeper的会话(Session)?会话超时时间是多久?
Zookeeper的会话(Session)是客户端与Zookeeper服务器之间的连接。当客户端第一次连接到Zookeeper服务器时,它会创建一个会话。Zookeeper的会话是有状态的,它可以在会话期间跨越多个操作。会话超时时间是指客户端在Zookeeper服务器上的会话失效之前允许的空闲时间。如果客户端在这段时间内没有发送心跳,Zookeeper服务器将认为客户端已经死亡,并终止会话
什么是Zookeeper的快照(Snapshot)和事务日志(Transaction Log)?
Zookeeper的快照(Snapshot)是Zookeeper保存的一个静态数据副本,用于在Zookeeper重启时恢复状态。事务日志(Transaction Log)则是用于保存Zookeeper中所有数据更新的序列化记录,可以在Zookeeper崩溃时用于恢复数据。快照和事务日志是Zookeeper实现高可用性和数据一致性的重要组成部分
什么是Zookeeper的客户端与服务器之间的心跳机制?
Zookeeper的客户端与服务器之间通过心跳机制来维持其连接状态。客户端在与服务器建立连接后,会定时向服务器发送心跳请求,以保证连接不会超时断开。同时,服务器也会定时向客户端发送心跳响应,以表明自己仍然存活。如果客户端在一定时间内没有收到服务器的心跳响应,则会认为服务器已经宕机,并尝试重新连接其他可用的服务器
Zookeeper如何处理网络分区(Network Partition)问题?
当出现网络分区时,Zookeeper采用的策略是通过选举机制来选择一个Leader节点,并使其负责处理所有的写请求。其他的节点成为Follower节点,只处理读请求并将写请求转发给Leader节点处理。当网络分区解决后,Follower节点会将所有的写请求发送给Leader节点,以便保证数据的一致性
如何使用Zookeeper实现服务发现和注册?
可以使用Zookeeper的节点监听机制来实现服务发现和注册。当服务启动时,在Zookeeper上创建一个持久节点,并在该节点下创建一个临时节点,节点名称为该服务的IP地址和端口号。其他服务可以监听该节点,并获取到注册的服务信息。
你如何使用Zookeeper监控集群状态?
可以使用Zookeeper的四字命令来监控集群状态。Zookeeper提供了一些命令,例如
stat、srvr和ruok等,可以通过Telnet或nc等工具连接Zookeeper服务器并执行这些命令,以获取集群状态和运行信息你如何使用Zookeeper实现分布式队列?
可以使用Zookeeper的有序节点(序列化节点)来实现分布式队列。当消息进入队列时,在Zookeeper上创建一个有序节点,并将消息存储在该节点上。其他消费者可以监听该节点,并获取到队列中的消息
你如何使用Zookeeper实现分布式配置管理?
可以使用Zookeeper的节点监听机制来实现分布式配置管理。当配置发生变化时,在Zookeeper上创建一个新的节点,并将新的配置信息存储在该节点上。客户端可以监听该节点,并获取到最新的配置信息。
Zookeeper的Znode的版本号是如何实现的?它有什么作用?
Zookeeper的每个Znode节点都有一个版本号,分为两种:数据版本(Data Version)和状态版本(Stat Version)。数据版本表示数据内容的版本号,每次对Znode节点数据进行修改都会使数据版本号加1。状态版本表示Znode的元数据版本号,例如Znode节点的子节点变化、ACL变化等都会引起状态版本号的增加。
Zookeeper的版本号是基于Zookeeper的ZAB协议实现的。当一个客户端请求对一个Znode节点进行操作时,Zookeeper会检查请求携带的版本号与Znode节点的当前版本号是否一致,若不一致则拒绝请求
版本号的作用是保证分布式环境下多个客户端对同一份数据进行修改时的一致性
什么是选举机制?
Zookeeper中的Leader选举是指在一个Zookeeper集群中,选举一个节点作为Leader,负责协调和管理整个集群的状态。Leader选举是Zookeeper保证分布式一致性的基础,如果Leader选举失败或出现异常,整个Zookeeper集群就无法正常工作。
Zookeeper中的Leader选举采用的是ZAB协议(Zookeeper Atomic Broadcast,Zookeeper原子广播协议)实现的。当一个节点启动时,它会与其他节点建立连接,然后尝试成为Leader。具体过程如下:
- 选举过程启动:节点开始向其他节点发送Leader选举的请求。
- 选票投票:节点收到其他节点的选举请求后,会投票给请求者,同时将自己的选票发送给其他节点。
- 统计选票:节点会定期统计选票,当一个节点收到的选票超过半数时,它就会成为Leader。
- 发布选举结果:当一个节点成为Leader后,它会向其他节点发送选举结果,告诉它们自己已经成为了Leader。
- 同步数据:当一个节点成为Leader后,它会将自己的数据同步给其他节点,确保所有节点的数据一致性。
需要注意的是,Leader选举并不是一次性的过程,而是一个持续的过程。如果当前的Leader宕机或出现网络分区等问题,会重新触发Leader选举过程,选举一个新的Leader来取代原来的Leader





