Zookeeper 常见面试问题
本文最后更新于 2024年1月10日 凌晨
Zookeeper 常见面试问题
Zookeeper 介绍
Zookeeper 是一个典型的分布式数据一致性的解决方案。
Zookeeper 从设计模式角度来理解,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生了变化,Zookeeper 就负责通知已经在 Zookeeper 上注册的那些观察者做出相应的反应。
Zookeeper 提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。它致力于为那些高吞吐的大型分布式系统提供一个高性能、高可用、且具有严格顺序访问控制能力的分布式协调服务。
Apache ZooKeeper 是一个开源的分布式协调服务,它为分布式应用提供了一致性保证,如同文件系统的目录树方式用来存储系统的元数据。ZooKeeper 是 Hadoop 和许多其他分布式系统的重要组件。
ZooKeeper的主要功能:
- 分布式数据一致性解决方案:
- ZooKeeper维护一个简单的键值对数据模型,确保跨集群的数据一致性和有序性。并且它保证了一系列的数据一致性特性(如顺序一致性、原子性和单一视图一致性)。
- 服务协调/集群管理:
- ZooKeeper可以跟踪集群中节点的状态,管理分布式应用中的critical section,并且能够进行领导选举(Leader Election)。
- 配置管理:
- 它允许集群中的分布式应用访问和更新配置信息。配置信息的更改将通知所有关注这些信息的客户端,以实现配置信息的实时更新。
- 命名服务:
- ZooKeeper可以为分布式系统提供命名服务,类似于DNS,允许客户端根据名称解析和查找主机。
- 同步服务:
- 它能够帮助客户端在分布式环境中进行同步,比如实现分布式锁和队列。
- 组成员管理:
- ZooKeeper能够管理组中的节点,例如它可以跟踪一个集群中的所有节点,并根据节点的加入和离开动态地更新其内部状态。
ZooKeeper的特性:
- 顺序一致性:
- 客户端请求的执行顺序与其发送顺序一致。
- 原子性:
- 更新操作要么完全成功,要么完全失败。
- 单一系统映像:
- 不管连接到哪个节点,客户端看到的数据视图都是一致的。
- 可靠性:
- 一旦事务完成,其结果就持久保存了。
- 实时性:
- 客户端总是能够读取到最新的数据状态。
- 等待无关:
- 对于来自不同客户端的请求,ZooKeeper保证不会因为某些特定的客户端请求缓慢或失败而阻碍其他快速的客户端请求。
典型应用场景:
- 保持配置信息的同步(如在Kafka和HBase等系统中)。
- 协调分布式应用,实施领导者选举,以决定哪个节点应该负责处理。
- 实现分布式锁,控制分布式环境中的资源访问。
- 构建分布式队列实现任务的分派和管理。
由于所有这些功能,ZooKeeper成为了支持大型分布式系统不可或缺的组件,尤其是那些需要快速、可靠的分布式协调服务的系统。
系统架构
ZooKeeper 是一个分布式协调服务,用于构建高可用、高性能的分布式系统。它采用集群架构,由多个服务器节点组成。其中,通过选举机制选择一个领导者(Leader),其他服务器则成为跟随者(Follower)。ZooKeeper 使用 ZAB 协议来实现数据复制和一致性,领导者负责处理客户端请求和事务。它还提供了层次化的数据存储模型和会话管理机制。ZooKeeper 的架构基于心跳机制来维护服务器之间的连接和存活状态。总体而言,ZooKeeper 提供了可靠的分布式协调服务,适用于构建分布式应用程序、分布式数据库等场景。
ZooKeeper 的架构包括以下几个核心组件:
- 领导者选举:
- 集群中的服务器通过选举机制选择一个领导者(Leader)。
- 选举过程包括服务器相互通信,协商选票等步骤。
- 选举完成后,选举出的领导者负责处理客户端请求和事务,其他服务器成为跟随者(Follower)。
- 数据复制:
- ZooKeeper使用ZAB(ZooKeeper Atomic Broadcast)协议实现数据的复制和一致性。
- 领导者接收写请求后,将其广播给所有跟随者。
- 跟随者确认并提交请求后,领导者告知跟随者可以应用该变更。
- 大多数服务器确认提交后,变更被认为是成功。
- 数据模型:
- ZooKeeper使用层次化的文件系统模型,类似于文件系统的树结构。
- 每个节点称为Znode,可以存储一小段数据,包括元数据和应用程序数据。
- 会话管理:
- ZooKeeper为每个客户端分配会话ID,用于维护与服务器的连接。
- 会话具有超时机制,如果客户端在一定时间内没有与服务器通信,会话将被终止。
- 心跳机制:
- ZooKeeper使用心跳机制维护服务器之间的连接和存活状态。
- 服务器在固定的时间间隔内相互发送心跳以检测对方的存活状态。
- 客户端库:
- ZooKeeper提供多种客户端库,如Java、C、Python等,方便开发人员与ZooKeeper集群进行交互。
- 客户端库负责将应用程序与ZooKeeper集群进行通信,进行读写操作等。
整体上,ZooKeeper 的架构采用了分布式一致性算法,并使用选举机制和复制策略来保证高可用性和数据一致性。集群中的服务器通过相互通信和协调,提供可靠的分布式协调服务。
部署模式
ZooKeeper 有三种部署模式:
- 单机模式(Standalone Mode):
- 单机模式是最简单的 ZooKeeper 部署模式。
- 在单机模式下,ZooKeeper 运行在一台服务器上,没有形成集群。
- 适用于一些小型或测试场景,不提供高可用性。
- 集群模式(Quorum Mode):
- 集群模式是 ZooKeeper 的典型部署模式,用于构建高可用性的分布式系统。
- 在集群模式下,ZooKeeper 运行在多台服务器上,彼此协同工作,形成一个 ZooKeeper 集群。
- 通过选举机制选择一个领导者,其他服务器成为跟随者。
- 集群模式提供了高可用性和容错性,适用于生产环境中的分布式系统。
- 伪集群模式(Pseudo-Distributed Mode):
- 伪集群模式是一种特殊的部署模式,用于在单台机器上模拟 ZooKeeper 集群。
- 在伪集群模式下,一台机器上运行多个 ZooKeeper 实例,每个实例使用不同的端口和数据目录。
- 这种模式通常用于开发、测试或学习,不具备真正的分布式特性,但可以模拟一些集群行为。
这三种部署模式可以根据实际需求选择,单机模式适用于简单的场景和测试环境,集群模式适用于生产环境中需要高可用性的分布式系统,而伪集群模式则用于本地开发和测试。
主从节点同步
ZooKeeper 使用 ZAB(ZooKeeper Atomic Broadcast)协议来保证主从节点的状态同步。ZAB 协议有两个阶段:领导者选举(恢复模式)和数据传播(广播模式)。
领导者选举(恢复模式):
- 当 ZooKeeper 集群启动或者领导者节点崩溃时,进入领导者选举过程。
- 集群中的每个服务器会向其他服务器发送投票请求,选举出一个新的领导者。
- 选出的领导者会成为新的数据的源头,其他节点成为它的跟随者。
数据传播(广播模式):
- 一旦选举完成,ZooKeeper 就进入广播模式。
- 领导者接收来自客户端的更新请求,并将这些更新请求转化为一个全局唯一的提案(proposal)。
- 领导者将提案发送给所有的跟随者。
- 当大多数的跟随者确认接收并处理了提案后,领导者可以提交提案,将其应用到本地状态。
通过 ZAB 协议,ZooKeeper 可以保证领导者和跟随者之间的状态一致性。在领导者选举过程中,选出的领导者能够包含了过去所有的事务。在数据传播阶段,领导者确保所有的服务器按照相同的顺序应用相同的事务,从而保持状态的一致性。
这样的设计保证了 ZooKeeper 的高可用性和一致性,即使在某些节点故障或者网络分区的情况下,仍然能够保持整个系统的稳定性。
通信机制
在 ZooKeeper 集群中,服务器之间通过 TCP 连接进行通信。通信的主要角色包括 Leader、Follower 和 Observer。
- Leader: Leader 负责处理客户端的读写请求,并协调整个集群的状态。Leader 会和每一个 Follower 和 Observer 建立 TCP 连接,以便进行数据同步和信息传递。
- Follower: Follower 是集群中的跟随者,它负责复制 Leader 的数据,并在 Leader 不可用时参与选举过程。Follower 与 Leader 之间通过 TCP 连接进行通信,Leader 将写请求广播给所有的 Follower。
- Observer: Observer 是一种特殊的跟随者,它不参与投票和选举过程,只负责复制数据。与 Follower 类似,Observer 也通过 TCP 连接与 Leader 通信。
在具体的通信过程中,涉及到数据同步、选举通知、心跳等操作。Leader 会维护与每个 Follower 和 Observer 的连接,并通过这些连接进行状态同步。通信的内容包括事务数据、提案(Proposal)和确认信息等。
总体而言,ZooKeeper 集群中的通信是基于 TCP 连接的,通过这种连接保持各个服务器之间的实时数据同步,确保集群的一致性和可用性。
通知机制
ZooKeeper 的通知机制是通过 Watcher(监视器)来实现的,Watcher 是一种事件机制,用于通知客户端关于 ZooKeeper 数据节点(znode)状态变化的事件。当一个 znode 的状态发生变化,与之相关的客户端将收到通知。
通知机制的基本原理如下:
- 客户端注册 Watcher:客户端在 ZooKeeper 上的某个 znode 上注册一个 Watcher,监视这个 znode 的状态变化。
- 事件触发:当该 znode 的状态发生变化,比如 znode 被创建、删除、数据更新等,ZooKeeper 会触发相应的事件。
- 通知客户端:ZooKeeper 将事件通知发送给注册了 Watcher 的客户端。
- 客户端处理事件:客户端收到通知后,可以在 Watcher 的回调方法中编写自定义的处理逻辑,例如更新缓存、重新获取数据等。
Watcher 是一次性的,即一旦触发了一次通知,就需要客户端重新注册 Watcher。客户端可以选择在每次处理完事件后重新注册 Watcher,以实现持续的监视。
通知机制使得客户端能够实时获取 znode 的状态变化,从而可以及时响应分布式系统中的各种事件,例如配置变更、节点上下线等。这对于构建分布式系统中的协调和通知非常重要。
分布式锁实现方式
分布式锁的实现方式是通过 ZooKeeper 的临时有序节点来实现的。以下是分布式锁的算法流程:
- 创建临时有序节点:客户端连接到 ZooKeeper,然后在锁的根节点(例如,
/lock
)下创建一个临时有序节点。ZooKeeper 会为每个节点分配一个唯一的序号,节点路径类似于/lock/lock-0000000000
,/lock/lock-0000000001
,以此类推。 - 获取子节点列表:客户端获取锁空间(
/lock
)下的所有子节点,并对这些节点按序号进行排序。 - 判断是否获得锁:客户端判断自己创建的节点是否是序号最小的节点。如果是,表示客户端成功获取锁;如果不是,表示需要监听比自己序号小一位的节点的变化。
- 监听前一节点的变化:如果客户端没有获得锁,它会监听刚好在自己之前一位的子节点的变化(例如,如果自己是
/lock/lock-0000000005
,则监听/lock/lock-0000000004
)。这是为了在前一节点的客户端释放锁时收到通知。 - 等待获得锁:客户端等待监听事件,如果收到前一节点被删除的通知,表示前一节点的客户端已经释放锁,客户端再次尝试获取锁,重复步骤 3。
- 执行业务逻辑:一旦客户端成功获得锁,就可以执行业务逻辑。
- 释放锁:业务逻辑执行完成后,客户端删除自己创建的临时有序节点,释放锁。
这样,通过 ZooKeeper 的临时有序节点和监听机制,实现了分布式锁的获取和释放。获得锁的客户端是有序的,只有序号最小的客户端能够成功获得锁,其他客户端需要监听前一节点的删除事件,以便在前一节点释放锁时重新尝试获取锁。这确保了在分布式环境下的锁的互斥性。
分布式一致性协议
Zookeeper 是一个为分布式应用提供一致性服务的软件,它主要用于协调和管理一个分布式系统中的数据。Zookeeper 内部使用了一种叫做 ZAB(Zookeeper Atomic Broadcast)的协议来保证分布式数据的一致性。
ZAB 协议(Zookeeper Atomic Broadcast)
核心特点:
- 原子广播:Zab 协议是一种原子广播协议,任何一次信息的广播和更新都是原子的,要么全部节点接收、要么全部不接收。
- 分布式一致性:它保证了一个集群在进行写操作时的正确顺序,并提供了对集群崩溃恢复后的状态进行同步的机制,从而保持集群状态的一致性。
- 领导者选举:Zab 协议在启动时或当当前领导者崩溃时,会自动进行新的领导者选举。
- 顺序保证:即使在网络分割或服务器故障的情况下,也可以保证一旦某数据更改被集群中的大多数成员接受,这个更改就永远不会消失。
Zab 协议主要用于 Zookeeper 中,对应用层而言是透明的,它提供的是一种发布/订阅机制保证了不同组件之间的状态同步。
与其他一致性协议的对比
Paxos:Paxos 是最广为人知的一致性协议之一。它是 Leslie Lamport 于 1990 年提出的用于解决分布式系统中的一致性问题的算法。Paxos 确保了即使在分布式系统中出现节点故障或网络问题的情况下,节点群体也能就某个值达成一致。
与 Zab 的对比:
- Paxos 的复杂性在于它的多阶段协商过程,这使得实现一个稳健且高效的 Paxos 系统较为困难。
- Zab 对比 Paxos,更加注重为分布式协调框架提供一个实用的一致性保证,同时 Zookeeper 设计时注重简单性和易理解性。
Raft:Raft 是另一个解决分布式系统中的共识问题的协议。它的设计目标之一就是比 Paxos 更容易理解和实现。
与 Zab 的对比:
- Raft 和 Zab 在很多方面都很类似,例如都在一开始进行领导者选举,之后所有的决策都通过领导者来协调。
- Raft 将一致性问题划分为几个子问题:领导者选举、日志复制等,这使得 Raft 可以更容易被理解和教学。
- Zab 专为 Zookeeper 设计,而 Raft 则可以在更多的系统中实现。
Multi-Paxos:Multi-Paxos 是 Paxos 协议的一个变体,它通过选择一个稳定的领导者来优化 Paxos 在多数值决定一致性时的效率问题。
与 Zab 的对比:
- Multi-Paxos 和 Zab 都选择了单个领导者来简化过程。
- Zab 是 Zookeeper 的专有协议,而 Multi-Paxos 在理论上是通用的,并被多种系统实现。
**VIEWSTAMPED REPLICATION (VSR/VRR)**:VRR 是另一种解决一致性问题的协议,它结合了视图更改(用于处理领导者更换)和状态机复制。
与 Zab 的对比:
- VRR 比较少见,而 Zab 在 Zookeeper 这种广泛使用的系统中实现,因此使用 Zab 的场景更多。
- VRR 和 Zab 同样使用领导者来处理请求,不过它们在处理领导者崩溃和网络分区等问题上的细节实现存在差异。
综合来看,每一种协议设计时都有其特定的优化目标和应用场景,选择哪一种协议取决于具体需求和系统设计考量。在实际应用中,由于 Zookeeper 的广泛使用以及其对一致性和性能的适中需求,Zab 成为了一个十分重要的分布式一致性协议。
ZAB 协议与 Paxo 算法
ZAB 协议与 Paxos 算法的异同:
相同点:
- Leader 选举: 在两者中,都涉及到了 Leader 的选举。Leader 负责协调和处理系统的操作。
- 超过半数原则: 在处理提案时,都需要获得超过半数的节点的认可或反馈。这确保了系统在正常运行时具有一致性。
- 值的提案: 两者都有一个值(或提案)来代表 Leader 的周期,即系统状态。
- 分布式系统: Paxos 和 ZAB 都是为构建分布式系统而设计的算法。
不同点:
- 设计目的:
- Paxos: 主要设计用于构建分布式一致性状态机系统,强调一致性。
- ZAB: 用于构建高可用的分布式数据主备系统,特别是用于 Apache ZooKeeper。除了一致性,还关注了可用性和分区容忍性。
- 提案的接受方式:
- Paxos: 通过多个阶段的提案和接受来实现一致性。
- ZAB: 强调原子广播,通过 Leader 广播的方式将操作原子性地传播给所有节点。
- 通信方式:
- Paxos: 通过多个阶段的消息通信来达成一致。
- ZAB: 强调 Leader-Follower 之间的通信,Follower 接受 Leader 的广播。
- 运行模式:
- Paxos: 通常是通过多个阶段的投票和接受来实现一致性。
- ZAB: 强调在分布式数据存储系统中的运行,主要关注数据的一致性。
总体而言,Paxos 和 ZAB 都是为了解决分布式系统中的一致性问题,但由于设计目的和运行环境的不同,它们在实现细节和应用场景上有所差异。
选举机制
ZooKeeper 的选举机制是分布式系统中关键的一环,保证了集群的高可用性与数据一致性。假设存在一个由五个服务器(Server 1~5)组成的 ZooKeeper 集群,以下是领导者选举的过程:
- 启动和投票:
- 当一台服务器(Server 1)启动时,它首先进入到寻找 Leader 的状态(LOOKING)。由于它是唯一运行的服务器,它会提名自己为 Leader 并开始发送选票给其他服务器。
- 当第二台服务器(Server 2)启动时,两个服务器互相交换选票。由于他们都没有历史数据,选举结果会基于它们的服务器 ID,Server 2(拥有更高 ID)将被提名,但直到超过半数服务器投票前,它们会保持 LOOKING 状态。
- 领导者选举:
- 第三个服务器(Server 3)加入时,根据大多数原则(超过半数的服务器同意),这时 Server 3 会成为集群的领导者(Leader),因为集群里现在有超过一半的服务器支持它。
- 随后的服务器启动:
- 第四和第五台服务器启动时,它们会收到前三台服务器的投票结果,由于已经有了现行的 Leader,它们会成为追随者(Follower)并结束 LOOKING 状态。
- 选举算法:
- ZooKeeper 使用的选举算法基于 Zxid(事务 ID)和 Myid(服务器 ID)。每个服务器在初次投票时都会提名自己,并将 (
Myid
,Zxid
)作为投票发送出去。 - 收到来自其他服务器的投票时,每个服务器将按照以下规则处理并可能更新其投票:
a. 比较 Zxid:更高的 Zxid 优先。
b. 若 Zxid 相等,则比较 Myid:更高的 Myid 优先。
- ZooKeeper 使用的选举算法基于 Zxid(事务 ID)和 Myid(服务器 ID)。每个服务器在初次投票时都会提名自己,并将 (
- 统计和决定:
- 每轮投票后,服务器会统计收到的票数。如果一条投票从超过半数的服务器那里获得支持,那么该投票对应的服务器将成为 Leader。
- 如果没有一条投票从超过半数的服务器那里获得支持,将继续进行新一轮的投票。
在 ZooKeeper 3.4.0 之后的版本中,使用了基于 TCP 协议的 FastLeaderElection 算法,它更加稳定和快速。此算法保证了在有 Leader 存在的情况下,新加入的服务器可以快速地同步最新的状态并成为 Follower。如果集群中不存在 Leader,将通过上述选举过程选择一个新的 Leader。选举过程中,优先考虑的是 Zxid,即数据版本,这确保了系统偏爱数据最新的服务器成为 Leader,有利于快速恢复和数据的最终一致性。
这个选举机制确保了在出现故障和恢复时,ZooKeeper 集群能够迅速恢复到一个有序和一致的状态。
连接断开和 Session 过期的处理
处理连接断开 (CONNECTIONLOSS) 异常:
- 等待重新连接: 当客户端遇到 CONNECTIONLOSS 异常时,它会尝试重新连接到 ZooKeeper 集群中的另一台服务器。这是由 ZooKeeper 客户端库自动处理的,无需额外操作。
- 重试操作: 一旦连接重新建立,客户端会尝试重新执行之前的 ZooKeeper 操作。这可能包括创建、读取、更新或删除节点等操作。由于 ZooKeeper 客户端库会维护操作的顺序,它可以确保在重新连接后按正确的顺序重试操作。
- 处理可能的数据不一致性: 由于 CONNECTIONLOSS 可能会导致一些操作在网络重新连接后重试,应用程序应该考虑数据的一致性。例如,在写入数据之前,首先检查节点是否已经存在,以避免重复创建。
处理 Session 过期 (SESSIONEXPIRED) 异常:
- 重新创建 ZooKeeper 客户端: SESSIONEXPIRED 表示客户端的会话已过期。在这种情况下,客户端需要重新创建一个新的 ZooKeeper 客户端。
- 恢复会话状态: 在重新创建 ZooKeeper 客户端时,如果应用程序需要保留先前的会话状态,可以使用先前的会话 ID 和密码来创建新的客户端。这样,先前创建的临时节点和 watches 等状态仍然有效。
- 重新执行初始化操作: 在创建新的 ZooKeeper 客户端后,应用程序可能需要重新执行一些初始化操作,例如重新设置 watches 或者重新创建一些需要持久化的节点。
总体而言,处理 CONNECTIONLOSS 和 SESSIONEXPIRED 异常需要确保客户端在连接断开或会话过期后能够正确地重新连接并继续执行必要的操作。由于 ZooKeeper 客户端库提供了自动重连和操作顺序的保证,开发者可以专注于处理特定的业务逻辑和数据一致性。
Watch 监听机制
Apache ZooKeeper 中的 Watch 机制是一种轻量级的事件通知机制,用于让客户端在它所关心的 ZooKeeper 节点发生特定变化时收到通知。Watch 对于构建分布式锁、配置管理、服务注册发现等功能尤为重要。
特性:
- 一次性通知: Watch 事件是一次性的。一旦设置的 Watch 被触发,它会向客户端发送一个通知,之后该 Watch 就失效了。如果客户端需要持续监控,则需要在处理 Watch 事件后重新设置新的 Watch。
- 会话期有效: Watch 与客户端的会话有关。在会话期间设置的 Watch 在会话有效期内都是有效的,但如果会话过期,所有设置的 Watch 都会被移除。
- 节点特定事件触发: Watch 可以针对各种节点事件设置,包括节点创建、删除、数据更新或子节点变更等。不同类型的 Watch 会在相应的事件发生时通知客户端。
- 客户端顺序保证: ZooKeeper 保证单个客户端会以顺序一致的方式看到它所设置的 Watch 事件,即客户端观察到的事件顺序与实际发生的顺序相符。
- 设定和通知分离: Watch 的注册是在读取操作中完成的,例如
getData()
、getChildren()
和exists()
,而通知是异步进行的。
注意事项:
一次性触发:由于 Watch 只会在被监控的节点发生变化时触发一次,因此客户端若想持续监听变化,必须在每次接收到 Watch 通知后重设 Watch。这种设计减少了服务器和网络的负载,因为它避免了不必要的长期监听。
网络延迟:客户端可能不会即时收到所有节点的变化通知。这是因为 Watch 事件有网络传输延迟,并且客户端需要在收到事件后重新注册新的 Watch。在节点数据变化频繁的情况下,客户端可能错过某些中间状态。
Watch 丢失:在某些极端情况下,Watch 事件可能会丢失(例如,如果客户端在设置 Watch 与事件被触发之间断开连接,而会话在恢复之前就已经过期)。
Watch 与读一致性:客户端读取数据时注册的 Watch 保证了客户端随后能观察到的所有数据变化,这意味着客户端读取到的数据至少与注册 Watch 时看到的数据一致。
实践中使用 Watch 时,开发者需要考虑一些最佳实践:
重新设置 Watch:在处理 Watch 事件的逻辑中,考虑立即重新设置 Watch,以便持续监听后续的节点变化。
异常处理:在网络或其他错误导致连接丢失的情况下,保证正确处理 Watch 事件,这可能包括在会话恢复后重新设置所有期望的 Watch。
数据新鲜度:对于需要读取节点上最新值的客户端,可以使用 sync()
方法来确保数据的最新性。
ZooKeeper 的 Watch 机制是设计得优雅且高效的,它为分布式应用提供了一个简单的方式来响应分布式状态的变化。然而,由于 Watch 的一次性和其他特性,合理使用 Watch 需要开发者严格遵守相关的最佳实践来保证应用的稳定和一致。
节点类型
- Znode 有两种类型:
短暂(ephemeral):客户端和服务器端断开连接后,创建的节点自己删除。
持久(persistent):客户端和服务器端断开连接后,创建的节点不删除。 - Znode 有四种形式的目录节点(默认是 persistent )
- 持久化目录节点(PERSISTENT) 客户端与 zookeeper 断开连接后,该节点依旧存在。
- 持久化顺序编号目录节点(PERSISTENT_SEQUENTIAL) 客户端与 zookeeper 断开连接后,该节点依旧存在,只是 Zookeeper 给该节点名称进行顺序编号。
- 临时目录节点(EPHEMERAL) 客户端与 zookeeper 断开连接后,该节点被删除。
- 临时顺序编号目录节点(EPHEMERAL_SEQUENTIAL) 客户端与 zookeeper 断开连接后,该节点被删除,只是 Zookeeper 给该节点名称进行顺序编号。
临时节点
在 ZooKeeper 中,临时节点(Ephemeral Nodes)是与客户端会话绑定的一种特殊类型的节点。这种节点具有以下特点:
特点
会话绑定:临时节点的生命周期与创建它的客户端会话相关。只要会话是活动的,临时节点就会存在。
会话过期自动删除:当客户端会话过期时,其创建的所有临时节点会被 ZooKeeper 自动删除。会话过期可能是由于客户端在会话超时时间内未能与服务器维持有效通信(例如未能发送心跳)而导致的。
不允许有子节点:ZooKeeper 不允许临时节点有子节点。这意味着你不能在临时节点下再创建任何节点(永久或临时)。
删除情况
临时节点会在以下情况下被删除:
会话过期:一旦客户端与 ZooKeeper 服务器之间的会话过期,所有与该会话关联的临时节点都会被自动删除。这种机制确保了客户端在意外断开连接后,它们在 ZooKeeper 上注册的任何临时状态都会在一定时间后清理。
客户端主动删除:与永久节点一样,客户端可以主动删除它自己创建的临时节点。
连接断开不删除
即使客户端与 ZooKeeper 服务器之间的连接临时中断,只要客户端能在定义的会话超时时间内重新连接,这期间创建的临时节点会继续保持。
注意事项:
- 在必要时,客户端必须重新连接到服务器以避免会话超时,以保留其临时节点。
- 应合理设置会话超时时间,以便在网络抖动等情况下,不会导致意外的临时节点删除。
- 临时节点通常用于构建具有失效转移能力的分布式锁,服务注册与发现等场景。
Watch 机制相关
关于临时节点与 Watch 机制的结合使用,有以下几个要点:
- 当临时节点因为会话过期而被删除时,任何在该节点上设置的 Watch 将被触发(如果有的话)。这对于监控资源的锁定状态或服务的可用性很有用。
- 如果客户端希望在连接临时中断后继续更新或检测临时节点的状态,它需要确保在会话超时之前重新连接并重新设置必要的 Watch。
- 客户端必须意识到它在创建的临时节点上设置的 Watch 是一次性的,节点变化后要重新设置 Watch。
综上所述,ZooKeeper 的临时节点是一种与客户端会话绑定的节点,它在失效或故障转移场景中非常有用。临时节点的设计确保了在客户端会话结束时可以清理其在集群中的临时状态。而与 Watch 机制相结合使用时,它提供了一个有效的机制来监控这些状态的改变,并在必要时作出响应。
机器扩容
在 ZooKeeper 中,动态进行机器扩容相对来说较为复杂,因为 ZooKeeper 的节点(Server)在启动时会通过选举机制选择一个 Leader,而这个 Leader 在集群中是固定的,不支持动态添加。
一种比较常见的做法是逐个重启(Rolling Restart):
- 添加新机器: 首先,将新的 ZooKeeper 服务器加入到集群中,确保其配置与现有服务器一致。
- 逐个重启: 逐个地重启现有的 ZooKeeper 服务器。在重启的过程中,每个服务器都会重新加入集群,并参与选举。由于新的机器已经在集群中,其状态可能会被选为 Leader 或 Follower。
- 等待选举完成: 等待每个服务器的选举过程完成,确保集群中有足够数量的节点选举出 Leader。这个过程可能需要一些时间,具体取决于选举算法、服务器的数量和网络延迟。
- 验证集群状态: 在整个过程完成后,验证集群的状态是否正常。可以使用 ZooKeeper 提供的命令行工具或 API 进行状态查询,确保新的机器已经成功加入并参与了选举。
需要注意的是,在这个过程中,客户端的连接可能会断开并重新连接到新的服务器。为了确保不影响应用程序的正常运行,通常建议在进行扩容时采取一些容错和重连机制,以保证客户端能够在集群变化时继续正常工作。
通知机制
ZooKeeper 的通知机制是由“Watcher”(监视点)实现的。Watcher 机制允许客户端在它们对某一个 znode 感兴趣的时候,向 ZooKeeper 服务器注册一个 Watcher。一旦 znode 发生变化,已注册的 Watcher 会触发,通知所有关心该 znode 的客户端。
通知机制的步骤如下:
- 注册 Watcher:
- 客户端在读取 znode 的内容时(例如使用
getData()
、getChildren()
方法),可以选择性地设置一个 Watcher 监视这个 znode。
- 客户端在读取 znode 的内容时(例如使用
- 单次触发:
- 注册的 Watcher 是一次性的,即一旦被触发,它就失效了。如果客户端需要持续获得 znode 的更新,它们必须在每次收到通知后重新注册 Watcher。
- 事件通知:
- 当 znode 的数据发生变化,或者它的子节点列表有更新的时候,ZooKeeper 会向所有注册了对应 znode 的 Watcher 的客户端发送通知。
- 处理通知:
- 客户端收到通知后,可以根据需要处理响应,比如读取更新后的数据。
- 重新注册 Watcher:
- 获得通知,完成一次读取之后,如果客户端希望继续监控该 znode 的未来变化,需要再次设置一个 Watcher。
这个通知机制是 ZooKeeper 实现分布式系统中数据变化监听的重要手段。需要注意的是,只有数据读取 API 才会给 Watcher 选项提供触发机会,对数据进行变更的 API 不会触发 Watcher。
此外,Watcher 通知是轻量级的,只告诉客户端 znode 发生了变化,但是不会告诉具体的细节。获取详细信息需要客户端主动去重新查询 znode 的状态。这个设计是为了减少网络流量和服务端的负载。监视点被设计为轻量级的并且是可扩展的,客户端有责任在接收到变化通知后更新本地的状态信息。
监听原理
ZooKeeper 中的监听原理涉及客户端(客户端程序或者 ZooKeeper 的客户端库)与 ZooKeeper 服务器之间的交互,以及客户端内部的线程间协作。这里具体介绍一下这个过程:
- 客户端初始化:
- 应用程序调用主函数(例如 Java 中的
main()
方法)创建一个 ZooKeeper 客户端实例。在创建客户端实例同时,通常会启动两个关键的线程或进程:
a. Listener 线程/进程(监听进程):负责监听从 ZooKeeper 服务器传回的事件通知。
b. Connect 线程/进程(网络连接/传输进程):负责管理与 ZooKeeper 服务器的网络连接以及数据传输。
- 应用程序调用主函数(例如 Java 中的
- 注册监听器:
- 客户端调用
getChildren()
、getData()
等方法时,可以选择是否注册一个 Watcher(监听器)。这些方法的调用被发送到 ZooKeeper 服务器,并在服务器上对应的节点上注册这个回调 Watcher。
- 客户端调用
- 监听器列表维护:
- ZooKeeper 服务器会在内部维护一个监听器列表,记录下哪些客户端对哪些 znode 注册了 Watcher,以及这些客户端的地址信息。
- 事件触发:
- 当一个被 Watcher 监视的 znode 发生变化(如数据的更改、删除,或子节点的变化)时,ZooKeeper 服务器将检查其内部的监听器列表,并找到所有对该事件感兴趣的 Watcher。
- 事件通知:
- ZooKeeper 服务器向在监听器列表中找到的客户端发送通知。这个通知将由 Connect 线程/进程接收,并转发给 Listener 线程/进程。
- 处理事件:
- Listener 进程接收到事件通知后,会调用注册的 Watcher 对象的
process()
方法。在这个方法中,客户端定义了对于各种事件的具体处理逻辑。
- Listener 进程接收到事件通知后,会调用注册的 Watcher 对象的
- 重新注册监听器:
- 值得注意的是,ZooKeeper 中的 Watcher 是一次性的,意味着一旦触发就失效。所以,如果客户端希望在同一 znode 上持续监听事件,它需要在处理事件之后再次注册新的 Watcher。
以上机制保证了 ZooKeeper 客户端能够及时响应 znode 的变化事件,并且可以根据需要更新自己的业务逻辑或状态。这是 ZooKeeper 实现分布式环境下的协同工作的关键特性。
常见端口的作用
2888:Follower 与 Leader 交换信息的端口。
3888:万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
节点掉线
ZooKeeper 集群遵循”过半机制”来保持集群的可用性和一致性。这意味着在集群中,只要有超过半数的节点存活,集群就能正常工作。
一台机器宕机:
在一个由3台机器组成的ZooKeeper集群中,如果一台机器宕机,剩下的两台机器仍然超过了总数的一半,因此,集群仍能继续工作。这种情况下,存活的两台机器能在宕机的机器恢复之前,维持集群的服务并处理客户端的请求。
两台机器宕机:
如果两台机器宕机了,那么集群将只剩下一台机器运行,这是少于总数的一半。因此,根据过半机制,集群将无法继续提供正常的服务。而且,在没有过半数节点确认的情况下,集群也不能完成Leader的选举过程,导致无法处理写请求。此时,集群会进入一个不可写的状态,直到足够的机器恢复,使得集群中的存活节点数再次超过总数的一半。
重要的是要了解,在ZooKeeper集群中,过半的节点存活不仅关系到读写服务的可用性,也是为了确保一致性。因为对于任何更改,必须由过半的节点存活并达成一致后才能提交,这样才能保证数据不会因为节点故障而丢失或出现不一致的情况。
事务性的支持
ZooKeeper 提供了对事务的支持,通过一系列的操作实现事务的原子性。以下是相关的函数和步骤:
- 操作初始化函数:
zoo_create_op_init
: 用于初始化创建节点的操作。zoo_delete_op_init
: 用于初始化删除节点的操作。zoo_set_op_init
: 用于初始化设置节点数据的操作。zoo_check_op_init
: 用于初始化检查节点的操作。
- 操作顺序:
- 客户端通过上述初始化函数创建需要的操作。
- 客户端可以通过多次调用这些初始化函数,生成一个包含多个操作的事务。
- 事务提交:
- 使用
zoo_multi
函数提交事务。该函数接受一个zoo_op
数组,每个元素对应一个操作。事务中的所有操作都将一起提交。
- 使用
- 原子性保证:
- ZooKeeper 服务会保证一次事务中的所有操作要么全部成功,要么全部失败。这确保了事务的原子性。
- 如果事务中的任何一个操作失败,整个事务将失败,且
zoo_multi
返回的状态信号将指示第一个失败的操作。
- 事务结果处理:
- 根据
zoo_multi
的返回值来判断事务是否成功。如果返回ZOK
,则整个事务成功;否则,根据返回值处理失败的情况。
- 根据
通过上述步骤,ZooKeeper 提供了一种简单而有效的方式来执行事务,并通过 zoo_multi
确保一组操作的原子性。这对于需要在分布式环境下保持数据一致性的应用场景非常有用。