跳至主要內容

Zookeeper 常见面试题


Zookeeper 的典型应用场景

Zookeeper 的典型应用场景包括:

  • 数据发布与订阅:这是 Zookeeper 的一种典型应用场景。发布者将数据发布到 ZooKeeper 节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。
  • 分布式锁:Zookeeper 可以用来实现分布式锁。利用 ZooKeeper 的节点唯一性和顺序性特点,可以保证多个线程在尝试获取锁时按照一定的顺序进行。
  • 集群管理:Zookeeper 可以帮助构建分布式系统,提供集群管理的功能,包括节点监控、异常处理、状态同步等。
  • 分布式协调:Zookeeper 可以为分布式系统中的各个参与者提供一个协调器,处理各种协调操作,例如任务分配、等待与通知等。
  • 命名服务:Zookeeper 可以作为一个分布式命名服务,为网络中的对象提供唯一的名称。

ZooKeeper 集群中服务器之间是怎样通信的

ZooKeeper集群中的服务器之间使用TCP协议进行通信,这个通信过程包括以下关键部分:

  • Leader和Follower:在ZooKeeper集群中,有一个服务器被选为领导者(Leader),其余服务器成为跟随者(Follower)。Leader负责处理所有客户端请求,而Followers主要用于接收Leader的数据更新。
  • 通信机制:每个服务器创建TCP连接,使用LearnerHandler实体来处理与其他服务器的网络通信。LearnerHandler的主要职责是接收来自其他服务器的数据更新,并将其传递给应用程序。
  • 客户端请求:客户端通过ZooKeeper客户端库连接到集群中的任一服务器,并向其中一个服务器发送请求。该服务器会将请求传递给Leader或直接处理请求。
  • 数据同步:Leader负责处理客户端请求,并将数据更新广播给所有Followers。Followers会应用这些更新以保持数据一致性。
  • 故障处理:如果Leader服务器发生故障,ZooKeeper使用选举机制来选举新的Leader,以确保集群的高可用性。

总之,ZooKeeper的通信原理是基于客户端/服务器模型的,客户端发送请求,服务器接收和处理请求,然后返回响应。这个通信过程确保了ZooKeeper集群中数据的一致性和高可用性。

zk集群数据是如何同步的

Zookeeper集群的数据同步过程如下:

  1. 集群启动时,会先进行领导者选举,确定哪个节点是Leader,哪些节点是Follower和Observer。
  2. Leader会和其他节点进行数据同步,采用发送快照和发送Diff日志的方式。
  3. 集群在工作过程中,所有的写请求都会交给Leader节点来进行处理,从节点只能处理读请求。
  4. Leader节点收到一个写请求时,会通过两阶段提交机制来处理。
  5. Leader节点会将该写请求对应的日志发送给其他Follower节点,并等待Follower节点持久化日志成功。
  6. Follower节点收到日志后会进行持久化,如果持久化成功则发送一个Ack给Leader节点。
  7. 当Leader节点收到半数以上的Ack后,就会开始提交,先更新Leader节点本地的内存数据。
  8. 然后发送commit命令给Follower节点,Follower节点收到commit命令后也会更新本地内存中的数据。
    这样,Zookeeper集群的数据同步就完成了。

Zookeeper 集群中是怎样选举leader的

zookeeper集群中服务器被划分为以下四种状态:

  • LOOKING:寻找Leader状态。处于该状态的服务器会认为集群中没有Leader,需要进行Leader选举;
  • FOLLOWING:跟随着状态,说明当前服务器角色为Follower;
  • LEADING:领导者状态,表明当前服务器角色为Leader;
  • OBSERVING:观察者状态,表明当前服务器角色为Observer。

Leader选举的触发时机

  • 集群启动,这个时候需要选举出新的Leader;
  • Leader服务器宕机;

第一次启动Leader选举

假设现在有三台机器搭建集群:

  1. 每个Server发出一个投票投给自己。当server1启动的时候,为Looking状态,对应的myid记为1,ZXID为0,他先投自己一票,此时他的投票为(1,0);然后需要把自己的选票发给集群中的其他机器。
  2. server2这个时候也启动了,也是Looking状态,也先投自己一票,也就是(2,0),然后需要把自己的选票发给集群中的其他机器。
  3. 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票是否有效,如检查是否是本轮投票、是否来自LOOKING状态的服务器;
  4. 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK的规则如下:
    1. 优先检查ZXID。ZXID比较大的服务器优先作为Leader;
    2. 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。
      对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
  5. 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经过半机器接收到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
  6. 改变服务器状态。一旦确定了Leader,每个服务器都会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
  7. 这个时候Server3启动了, 发现集群中已经有了Leader,就建立连接并把自己的状态置为Following。

非第一次启动Leader选举

在zookeeper运行期间,即便有新服务器加入,也不会影响到Leader,新加入的服务器会将原有的Leader服务器视为Leader,进行同步。但是一旦Leader宕机了,那么整个集群就将暂停对外服务,进行新一轮Leader的选举,其过程和启动时期的Leader选举过程基本一致。假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Leader挂了,此时便开始Leader选举。这里我们假设server3为原本的Leader,其余四台均为Follower,某一时刻server3和server5都宕机了,那么选举过程如下:

  1. 变更状态。Leader宕机后,余下的非Observer服务器都会将自己的服务器状态变更为LOOKING,然后开始进行Leader选举流程;
  2. 每个Server会发出一个投票。在这个过程中,需要生成投票信息(Epoch,ZXID,myid),对应的server1,server2,server4的选票信息如上图,;其中Leader选举的规则是
    1. EPOCH大的直接胜出
    2. EPOCH相同,事务大的胜出
    3. 事务ID相同,服务器ID大的胜出
      之后各个Looking状态的服务器会交换信息,最终会多数选举server4,也就更改为(1,120,4)的选票信息。
  3. 接收来自各个服务器的投票。与启动时过程相同;
  4. 处理投票;
  5. 统计投票;
  6. 改变服务器的状态,此时server4成为新的Leader。

zk 节点宕机如何处理

Zookeeper 必须集群部署,推荐配置不少于 3 个节点。Zookeeper 自身也要保证当一个节点宕机时,其他节点会继续提供服务。

  • 如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数据是有多个副本的,数据并不会丢失。
  • 如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。

因此,Zookeeper 可以容忍一部分节点的宕机,但前提是要保证超过一半的节点是正常工作的。在这种情况下,Zookeeper 集群仍然可以正常提供服务。如果 Zookeeper 节点挂得太多,只剩一半或不到一半节点能工作,集群就会失效。

Zookeeper脑裂是什么原因导致的

Zookeeper脑裂是指一个集群环境中出现了多个Master节点,导致数据不一致和数据问题。这种情况通常发生在网络故障导致集群中部分节点失去与Master节点的连接,而在这些节点看来,Master节点已经失效,因此它们会选举新的Master节点。在这个过程中,可能会出现多个Master节点,导致脑裂问题。

脑裂问题的主要原因是网络环境不稳定和假死现象。例如,当集群中的部分节点由于网络原因无法连接到Master节点时,这些节点会认为Master节点已经失效,从而发起新的选举。然而,如果网络不稳定,可能会出现Master节点重新进入集群的情况,导致出现多个Master节点。

解决Zookeeper脑裂问题的方法包括:

  • 增加网络稳定性,避免网络故障导致的问题。
  • 调整Zookeeper集群的选举机制和超时设置,使其更适应网络环境和集群架构。
  • 及时发现并处理多个Master节点,避免数据不一致和其他问题的发生。

Zookeeper 是如何解决脑裂问题的

Zookeeper的解决方案是通过过半机制来避免脑裂问题的发生。

具体来说,在Zookeeper的领导者选举过程中,如果某台ZookeeperServer获得了超过半数的选票,则此ZookeeperServer就可以成为Leader。

举个简单的例子,如果现在集群中有5台ZookeeperServer,那么过半数就是2.5,也就是说,领导者选举的过程中至少要有三台ZookeeperServer投了同一个ZookeeperServer,才会选出来一个Leader。

过半机制中是大于而不是大于等于,目的是为了防止脑裂。因为如果投票结果是大于等于2.5,那么只要有两台机器同时发生故障,就能导致过半数失效,从而引发脑裂问题。而如果是大于2.5,则必须有一台机器故障的同时,另外还有一台机器也故障,才能导致过半数失效,这样就能大大降低脑裂问题的发生概率。

因此,Zookeeper通过过半机制来保证在发生脑裂问题时,集群仍然能够正常工作,避免因为脑裂问题导致集群失效的问题。

为什么Zookeeper集群的数目一般为奇数个

Zookeeper集群的数目一般为奇数个,主要是出于以下两方面的考虑:

  • 防止脑裂。如果Zookeeper集群的节点数为偶数个,那么在集群发生脑裂并分成两个均等子集群的情况下,可能导致整个Zookeeper集群都无法正常工作。而如果集群节点数为奇数个,则即使发生脑裂并分成两个子集群,每个子集群都至少会有一个节点可以正常工作,从而保证集群的可用性。
  • 便于选举。在Zookeeper集群中,需要选出一个新的leader。在奇数个节点的情况下,当节点数量的一半比另一半多1时,多的那个节点就可以作为投票箱中的一票,确保了选举过程的顺利进行。如果节点数为偶数个,可能会出现半数以上节点与另一半节点投票结果不一致的情况,增加选举的复杂性。

因此,为了确保Zookeeper集群的稳定性和可用性,通常采用奇数个节点。

Zookeeper 是如何保证事务的顺序一致性的

Zookeeper保证事务的顺序一致性是通过两个机制实现的:事务的严格串行化和锁定机制。

  • 事务的严格串行化:在Zookeeper中,所有的写操作都必须经过一个称为“提交”(commit)的阶段。在一个事务还没有被提交之前,任何客户端都无法读取该事务。 这种机制确保了所有事务按照严格的顺序执行,保证了事务的顺序一致性。
  • 锁定机制:Zookeeper使用锁定机制来保证事务的顺序一致性。在某些情况下,不同的事务可能需要访问同一个资源,这时就需要使用锁定机制来协调它们之间的访问。Zookeeper提供了两种类型的锁:共享锁和排他锁。共享锁允许多个事务同时访问同一个资源,而排他锁则阻止其他事务访问该资源。

通过以上两个机制,Zookeeper确保了事务的顺序一致性,从而保证了数据的一致性和系统的可靠性。

说说 Zookeeper 的 CAP 问题上做的取舍

Zookeeper 在 CAP 问题上选择了 CP,也就是说,在一致性和可用性之间,Zookeeper 选择了强一致性。

Zookeeper 是一个分布式协调服务,它的主要职责是维护整个系统的状态,并提供一致性的服务。因此,为了保证数据的一致性,Zookeeper 必须确保在分布式节点之间进行同步的时候,不能出现不一致的情况。这就意味着,在出现网络故障或者消息丢失的情况下,Zookeeper 不能牺牲用户的体验,也就是不能出现部分节点可用而部分节点不可用的情况。

然而,这并不意味着 Zookeeper 完全牺牲了可用性。尽管它选择了一致性优先的策略,但它还是通过一些机制来尽可能地提高可用性。例如,它允许客户端在多个服务器之间进行备份,以防止单个服务器故障导致服务不可用。此外,它还提供了实时性和持久性的选择,以满足不同应用场景的需求。

总的来说,Zookeeper 在 CAP 问题上的取舍是为了保证数据的一致性,虽然在一定程度上牺牲了可用性,但通过一些机制尽可能地提高服务的可用性。

讲一下 ZooKeeper 的持久化机制

Zookeeper的持久化机制主要涉及两种数据存储方式:内存存储和磁盘存储。

  • 内存存储:这是Zookeeper默认的数据存储方式。在内存存储中,Zookeeper将所有数据保存在内存中,而不是磁盘上。当Zookeeper关闭或发生故障时,内存中的数据会丢失。为了提高性能,Zookeeper采用了延迟写盘的策略,即将数据先写入内存,再异步地写入磁盘。这种方式可以减少磁盘I/O操作,提高系统的响应速度。
  • 磁盘存储:为了解决内存存储数据丢失的问题,Zookeeper提供了磁盘存储的持久化方式。在磁盘存储中,Zookeeper将数据保存在磁盘上,并在启动时从磁盘加载数据。为了提高写入性能,Zookeeper采用了两种机制:快照(snapshot)和事务日志(transaction log)。快照是Zookeeper数据树的一个完整拷贝,而事务日志记录了所有的数据更改操作。通过这两种机制,Zookeeper可以在系统故障时恢复数据。

Zookeeper的持久化机制可以根据实际需求进行选择和配置。对于需要高可用性和实时性的场景,可以选择内存存储;对于需要更可靠性和可恢复性的场景,可以选择磁盘存储。

Zookeeper 分布式锁如何实现

详情可以看这篇文章ZK分布式锁

ZooKeeper可以用于实现分布式锁,主要的思路是利用ZooKeeper的临时顺序节点唯一性和顺序性的特点来实现。具体实现步骤:

  1. 创建一个ZooKeeper节点作为锁的根节点,例如 /locks。
  2. 当一个线程需要获取锁时,它在锁的根节点下创建一个临时顺序节点,例如 /locks/lock-0001,并尝试获取锁。
  3. 线程获取锁的过程是通过检查它创建的节点是否是锁节点下最小的节点,如果是,则表示该线程获得了锁;否则,线程监听它创建节点的前一个节点,等待前一个节点的释放。
  4. 当线程释放锁时,它删除自己创建的节点。

这种方式实现的分布式锁具有一定的可靠性和性能,但需要确保ZooKeeper集群的可用性和性能足够好。同时,需要处理连接断开、会话过期等异常情况,以保证分布式锁的正确性。此外,还可以使用ZooKeeper的超时机制来处理死锁等问题

Zookeeper 的通知机制是什么

Zookeeper的机制可以感知某些事件的发生,并将这些事件通知给客户端。这种通知机制是异步的,也就是说,当事件发生时,Zookeeper会选择一种方式将事件通知给客户端,而不需要等待客户端的回应。

具体来说,Zookeeper允许客户端向服务端的某个Znode注册一个Watcher监听,当这个Znode的一些指定事件(如数据改变、节点删除、子节点状态变更等)发生时,Zookeeper就会通知客户端。这种Watcher机制使得Zookeeper能够实现分布式锁、发布订阅等功能。

另外,Zookeeper的机制也允许客户端在每个Znode节点上设置一个观察,如果被观察的Znode节点有变更,那么这个观察就会被触发,然后Zookeeper就会将这个观察所属的客户端接收一个通知包,告知节点已经发生了什么事件。

Zookeeper 对节点的 watch 监听通知是永久的吗

在 ZooKeeper 中,节点监视(watch)通知不是永久的,而是一次性触发。当客户端注册一个监视事件,例如监视一个节点的数据变化或子节点的变化时,如果相应的节点状态发生了变化,ZooKeeper 会通知客户端,并触发监视事件。一旦监视事件被触发,它会从客户端的监视列表中删除,客户端需要再次显式设置监视,以便继续监视节点的状态变化。这种一次性的监视通知机制有助于避免客户端在不再需要监视的情况下浪费资源。如果客户端需要持续监视节点状态的变化,它必须在每次触发监视后重新设置监视,以确保不会错过后续的变化通知。

值得注意的是,从 ZooKeeper 版本 3.5.0 开始,引入了永久监听(persistent watch)的支持。这意味着一旦触发了监听事件,除非客户端显式取消监听,否则 ZooKeeper 将持续向客户端发送通知,而不仅仅触发一次。这对于需要持续监视节点状态的情况非常有用,客户端不需要反复设置监视,可以持续接收到节点状态的变化通知。

这个改进提高了 ZooKeeper 的灵活性和适用性,允许开发人员更精细地控制监视行为,以满足不同应用场景的需求。