Zookeeper 知识点整理

本文详细介绍了ZooKeeper的数据模型、Watcher机制、一致性原理及其实现算法,包括CAP理论、ZAB协议等,同时探讨了ZooKeeper在数据发布/订阅、统一命名服务、分布式锁和分布式队列等场景中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZooKeeper 是分布式协调服务,为分布式系统提供一致性服务。其一致性是基于 Paxos 算法的 ZAB 协议完成的。可以在分布式中共享配置,协调锁资源,提供命名服务等。

集群是加机器,并多台运行相同的系统;分布式是业务拆分,将系统拆分成多个子系统,然后部署在不同的服务器上。Zookeeper主要解决各个分布式组件的协调,减少各个系统之间的耦合度,配置整个分布式系统和分布式事务的处理等。

1. 数据模型

数据模型类似文件系统,可以看做是一颗树,每个节点叫做ZNode,每一个节点可以通过路径来标识。ZNode包含数据,访问权限,元数据和子节点的引用。

  1. data:Znode存储的数据信息。
  2. ACL:记录Znode的访问权限,即哪些人或哪些IP可以访问本节点。
  3. stat:包含Znode的各种元数据,比如事务ID、版本号、时间戳、大小等等。
  4. child:当前节点的子节点引用,类似于二叉树的左孩子右孩子。

Zookeeper是为读多写少的场景所设计,所以ZNode不是用来存储大规模业务数据,而是用于存储少量的状态和配置信息,每个节点的数据最大不能超过1MB。

1.1 节点属性

状态属性描述
cZxidCreated ZXID,表示该节点被创建时的事务ID
ctimeCreated Time,表示该节点被创建的时间
mZxidModified ZXID,表示该节点最后一次被更新时的事务ID
mtimeModified Time,表示该节点最后一次被更新的时间
pZxid表示该节点的子节点列表最后一次被修改时的事务ID。只有子节点列表变更才会变更pZxid,子节点内容变更不影响pZxid
cversion子节点的版本号(子节点每修改一次递增加1)
dataVersion当前节点版本号(每修改一次递增加1)
aclVersion当前节点的ACL版本号(节点每修改acl权限递增加1)
ephemeralOwner临时节点标识。如果当前节点是临时节点,则存储的创建者的sessionID;如果不是,则为0。
dataLength当前节点所存储的数据长度
numChildren当前节点的子节点个数

ZXID
在Zookeeper中,事务是指能够改变服务器状态的操作,一般指节点创建与删除、节点内容更新和客户端会话创建与失效等操作。对于每一个事务请求,都会分配一个全局唯一的事务ID,即ZXID。每一个ZXID对应一次更新操作,所以可从ZXID中了解到更新请求的全局顺序。每个Znode节点都有3个zxid属性,包括cZxid、mZxid 和 pZxid。

ZXID是一个全局有序Long型(64位)整数,分为两部分:纪元(epoch)和计数器(counter)。纪元(epoch)代表当前集群所属的哪个Leader,计数器(counter)是一个递增的数字。

Version
每个Znode节点都有3个version属性,包括cversion、dataVersion 和 aclVersion 。Znode中的数据可以有多个版本,如果某一个节点下存有多个数据版本,那么查询这个节点数据就需要带上版本号。

1.2 类型

  1. 持久节点 (PERSISTENT):默认的节点类型。创建节点的客户端与Zookeeper断开连接后,该节点依旧存在 。
  2. 持久顺序节点(PERSISTENT_SEQUENTIAL):在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;创建节点的客户端与Zookeeper断开连接后,该节点依旧存在 。
  3. 临时节点(EPHEMERAL):创建节点的客户端与Zookeeper断开连接后,该节点会被删除。
  4. 临时顺序节点(EPHEMERAL_SEQUENTIAL): 在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与Zookeeper断开连接后,该节点会被删除。

创建节点命令

  1. 持久节点:create path data
  2. 持久顺序节点:create -s path
  3. 临时节点:create -e path data
  4. 临时顺序节点:create -e -s path

1.3 ACL

Zookeeper通过ACL(Access Control List,节点的权限控制)机制来解决Znode节点的访问权限问题。要注意的是Zookeeper对权限的控制是基于Znode级别的,也就说节点之间的权限不具有继承性,即子节点不继承父节点的权限。

zookeeper中设置ACL权限的格式由<schema>:<id>:<acl>三段组成。

schema 表示授权的方式.

  1. world:任何人都可以访问;
  2. auth:只有认证的用户可以访问;
  3. digest:使用用户密码生成MD5哈希值作为认证ID;
  4. host/ip:使用客户端主机IP地址来进行认证。

id 表示权限的作用域,用来标识身份,依赖于schema选择哪种方式。

acl 表示给一个节点赋予哪些权限,节点的权限有create,、delete、write、read、admin 统称cdwra。

2. Watcher

在Zookeeper中允许客户端向服务端注册一个Watcher,Watcher是注册在特定Znode上的监听器,它可以监控节点的数据变化以及子节点的变化。客户端在请求读操作的时候,可以选择是否设置Watcher。当服务端的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个异步事件通知来实现分布式通知功能。这个监听器是一次性的,如果还要继续监听这个节点,就需要在客户端的监听回调中,再次对节点的监听事件设置为True。

类型

  1. Data Watches:监听节点的数据变更,触发条件getData()、exists()、setData()、create()。
  2. Child Watches:监听子节点发生变更触发,触发条件getChildren()、create()。

在调用delete()方法删除节点时,会触发Data Watches。如果被删除的节点还有父节点,还会触发一个Child Watches。

监听命令

  1. 监听节点内容变化:get -e -w path
  2. 监听节点状态变化:stat -w path
  3. 监听该节点下所有子节点的增减:ls -w path

3. 一致性

3.1 CAP

在分布式系统中,因为分区容错性(Partition Tolerance)的存在,就必定要求我们需要在一致性(Consistency)和可用性(Availability)中做出权衡。

  1. 分区容错性:某节点或网络分区出现故障,导致无法通信。
  2. 一致性:写操作后的读操作,所有节点在同一时间的数据完全一致。
  3. 可用性:保证每个请求不管成功或者失败都有响应。

因为分区容错性的存在,即可能出现通信失败,所以一致性和可用性不能同时成立。

  1. 为了保证一致性,在写操作时,要锁住其他节点的读写操作。只有数据同步后,才能重新开放读写。锁定期间,其他服务器不能读写,就没有可用性了。
  2. 为了保证可用性,在写操作时,就不能锁住其他节点,就没有一致性了。

所以系统设计只能选择一个目标。如果追求一致性,那么无法保证所有节点的可用性;如果追求所有节点的可用性,那就没法做到一致性。Eureka 保证了AP,而 ZooKeeper 保证了CP。

3.2 协议与算法

拜占庭将军问题指出在不可靠信道上试图通过消息传递的方式达到一致性是不可能的,所以一致性算法的必要前提就是安全可靠的消息通道。在分布式系统的整个调用链中,所有服务的数据处理要么都成功要么都失败,即所有服务的原子性问题。

3.2.1 2PC(两阶段提交)

在两阶段提交中,主要涉及到两个角色,分别是协调者和参与者。

  1. 第一阶段:执行一个分布式事务的时候,事务发起者先向协调者发起事务请求,然后协调者给所有参与者发送 prepare 请求。参与者收到请求后,就需要执行事务了,但不提交,并将 Undo 和 Redo 信息记录到事务日志中。之后参与者向协调者反馈是否准备好了。
  2. 第二阶段:协调者根据参与者反馈的情况决定进行事务提交或回滚操作。
    如果协调者收到了所有参与者准备好了的响应,那么就进行事务的提交,协调者向所有参与者发送 commit 请求。参与者收到请求后,执行事务的提交操作,完成后会给协调者返回响应。协调者收到所有参与者返回的事务提交成功的响应之后则完成事务。
    如果协调者并没有收到所有参与者准备好了响应,那么就进行事务的回滚,协调者向所有参与者发送 rollback 请求。当参与者收到请求后,执行事务的回滚操作,完成后会给协调者返回响应。协调者收到响应后便给事务发起者返回处理失败的结果。

缺点

  1. 单点故障,如果协调者挂了那么整个系统都不可用。
  2. 阻塞。第一阶段,协调者发送 prepare 请求,参与者收到后进行事务的处理但并不提交,导致一直占用资源不释放。如果此时协调者挂了,那么这些资源就不会再释放,极大地影响性能。
  3. 数据不一致。第二阶段,协调者发送了部分 commit 请求后就该挂了,导致收到请求的参与者进行事务的提交,而没有收到的参与者不会进行事务的提交,使得产生的数据不一致。

2PC

3.2.2 3PC(三阶段提交)

因为2PC存在单点,容错机制缺陷等问题,从而产生了 3PC。

  1. 第一阶段 CanCommit:协调者向所有参与者发送 CanCommit 请求,参与者收到请求后会根据自身情况判断是否能执行事务,如果可以则返回 YES 并进入预备状态,否则返回 NO。
  2. 第二阶段 PreCommit:协调者根据参与者返回的响应来决定是否进行 PreCommit 操作。
    如果参与者返回都是YES,那么协调者向所有参与者发送 PreCommit 预提交请求。参与者收到请求后,开始执行事务,但不提交,并将 Undo 和 Redo 信息记录到事务日志中。之后参与者向协调者反馈是否成功的响应。
    如果有一个参与者返回 NO,或者在一定时间内没有收到全部参与者的响应,那么就会中断事务,并向所有参与者发送 abort 中断请求。参与者收到中断请求后会立即中断事务,或者一定时间内没有收到协调者的请求,也会自动中断事务。
  3. 第三阶段 DoCommit:协调者根据参与者反馈的情况决定进行事务提交或回滚操作。
    如果协调者收到了所有参与者 YES 响应,那么就进行事务的提交,协调者向所有参与者发送 commit 请求。当参与者收到请求后,执行事务的提交操作,完成后会给协调者返回响应。协调者收到所有参与者返回的事务提交成功的响应之后则完成事务。
    如果协调者收到了任何一个 NO 或一定时间内没有收到所有参与者的响应,那么就会中断事务,并向所有参与者发送 abort 中断请求。参与者收到中断请求后进行事务回滚,并向协调者反馈回滚状况,协调者收到参与者返回的消息后,中断事务。

优点

  1. 超时中断。通过一系列的超时机制缓解了阻塞问题,比如协调者未在指定时间内为收到全部的确认消息则进行事务中断的处理。

缺点

  1. 数据不一致。在第三阶段 DoCommit,如果参与者收到了请求之后其他参与者和协调者挂了或者出现网络分区,那么收到消息的参与者都会进行事务提交,导致数据不一致。

3PC

3.2.3 Paxos

Paxos 算法是基于消息传递且具有高度容错特性的一致性算法,其解决在分布式系统中将某个值达成一致 。

角色

  1. Proposer:提案者。提出提案 Proposal,提案信息包括提案编号和提议的值。
  2. Acceptor:表决者。参与决策,回应提案者的提案。收到提案后可以接受提案,若提案获得多数表决者的接受,则该提案被批准了。
  3. Learner:最终决策学习者。不参与决策,从 Proposers/Acceptors 学习最新达成一致的提案。

阶段

  1. 第一阶段 Prepare:Proposer 提案者提出提案 proposal,每个提案者在提出提案时会获取到一个全局唯一且递增的提案编号 N,然后发送给所有的表决者。每个表决者仅接受大于本地最大编号 maxN 的提案,并返回之前批准过的最大提案编号和提案内容响应给提案者,如果没有就表示批准。
  2. 第二阶段 Accept 一:如果提案者收到了半数表决者的接受(提案者本身同意),那么提案者会给所有的表决者发送真正的提案,包括提案编号和内容。表决者会再次比较自身已批准过得最大提案编号,如果该提案编号大于等于已批准过的最大提案编号,就会批准该提案,即执行提案内容但不提交。之后将情况返回给提案者,如果不满足则不响应或返回NO。
  3. 第二阶段 Accept 二:如果提案者收到超过半数表决者的批准,就会向所有表决者发送提案的提交请求,包括未批准的表决者,会无条件执行和提交。如果没有收到,就会递增该提案的编号,再次进入 Prepare 阶段。
  4. 第三阶段:提案者的提案完成,将完成的提案发送给所有最终决策学习者。

死循环问题
提案者 P1 提出提案 M1,完成了 Prepare 阶段,表决者批准了 M1。提案者 P2 同时提出提案 M2,完成了 Prepare 阶段,表决者也批准了 M2。之后 P1 的方案就不能在 Accept 阶段中被半数的表决者批准,因为表决者批准了比 M1 更大的 M2。之后 P1 通过自增方案变为 M3 重新进入 Prepare 阶段,然后表决者又批准了 M3,导致 M2 批准失败,M2 就自增又进入 Prepare 阶段。

3.2.4 Raft

详见:Raft 协议

4. ZAB

Zookeeper集群是一主多从的结构。在更新数据时,首先更新到主节点,再同步到从节点;在读取数据时,直接读取任意从节点。Zookeeper采用ZAB协议来保证主从节点的数据一致性。

4.1 集群角色

  1. Leader:同一时间集群总只允许有一个Leader,提供对客户端的读写功能,负责将数据同步至各个节点;
  2. Follower:提供对客户端读功能,写请求则转发给Leader处理,当Leader崩溃失联之后参与Leader选举;
  3. Observer:手动配置,观察集群的最新状态变化,并将状态变更同步过来。与Follower基本一致,提供对客户端读功能,但不参与Leader选举,实现动态扩展Zookeeper集群又不会降低写性能。

4.2 节点状态

Zookeeper是通过自身的状态来区分自己所属的角色,来执行自己应该的任务。

  1. LOOKING:当节点认为群集中没有Leader,服务器会进入LOOKING状态,目的是为了查找或者选举Leader;
  2. LEADING:Leader角色;
  3. FOLLOWING:Follower角色;
  4. OBSERVING:Observer角色。

4.3 ZAB状态

整个ZAB协议主要包括崩溃恢复和消息广播两个过程。Zookeeper给ZAB定义了四种状态,反应了从选举到对外提供服务的过程中的四个步骤。

  1. ELECTION:集群进入选举状态,此过程会选出一个节点作为Leader角色;
  2. DISCOVERY:连接并响应Leader心跳,并检测Leader的角色是否更改,发现最新的ZXID和事务日志。通过此步骤之后选举出的Leader才能执行真正职务;
  3. SYNCHRONIZATION:整个集群都确认Leader之后,会把Leader的数据同步到各个节点,保证整个集群的数据一致性,通过此步骤之后选举出的Leader才能执行真正职务;
  4. BROADCAST:过渡到广播状态,集群开始对外提供服务。
4.3.1 ELECTION(选举)

发生集群选举有两种情况:

  1. 服务启动的时候,整个集群没有Leader节点就会进入选举状态;
  2. 在服务运行中,可能会出现服务宕机、断电、网络延迟等情况,这时候Leader就不能再对外提供服务了,当其他节点通过心跳检测到Leader失联之后,集群也会进入选举状态。
4.3.1.1 服务器启动时期的Leader选举
  1. 发送投票。初始情况下,集群没有Leader,服务器都是LOOKING状态。服务器会先投给自己,再把这个投票发给集群的其他所有机器。投票包含服务器的myid和ZXID;
  2. 接收投票。服务器接收其他服务器的投票。首先会判断该投票的有效性,包括检查是否是本轮投票,是否来自LOOKING状态的服务器;
  3. 处理投票。服务器接收投票后进行处理,将其他服务器的投票和自己的投票进行比较,发送比较后的投票;
  4. 统计投票。投票完,服务器会统计所有投票。当有过半的服务器接收到了相同的投票信息,就算选出了Leader;
  5. 改变状态。确定了Leader,每个服务器更新自己的状态。如果是Leader,变更为LEADERING;如果是Follower,变更为FOLLOWING。
4.3.1.2 服务器运行期间的Leader选举

选出Leader一般不会再发生变化,除非Leader挂了,就会进入新一轮的选举。

  1. 改变状态。Leader挂了,除了Observer服务器,其余服务器变更为LOOKING状态,进入选举状态;
  2. 后续流程与服务器启动时期的Leader选举一致。发送投票,接收投票,处理投票,统计投票,改变状态。
4.3.1.3 投票比较规则
  1. 选高的纪元(epoch);
  2. 纪元(epoch)相同,选大的ZXID,ZXID越大代表该节点的数据越完整;
  3. 纪元(epoch)和ZXID都相等,比较serverId。serverId是配置Zookeeper集群所配置的,所以配置Zookeeper集群的时候可以把服务性能更高的集群的serverId配置大些,让性能好的机器担任Leader角色。

epoch:纪元,表示投票的次数。同一轮投票过程中的epoch是相同的,每投完一次票这个数据就会增加。
Server id(或sid):服务器ID,编号越大在选择算法中的权重越大。

4.3.2 DISCOVERY(发现)

发现阶段,服务器连接并响应Leader心跳,并检测Leader的角色是否更改。通过此步骤之后选举出的leader才能执行真正职务,从节点中发现最新的ZXID和事务日志。

  1. Leader会接收所有Follower的纪元(epoch);
  2. Leader接收到过半的epoch值后,从中选出最大的epoch,然后对其加1,在分发给各个Follower;
  3. 各个Follower收到全新的epoch后,返回ACK给Leader,带上各自最大的ZXID和历史事务日志;
  4. Leader接收到过半的ACK后,从中选出最大的ZXID,并更新自身的历史事务日志。
4.3.3 SYNCHRONIZATION(同步)

同步阶段,把Leader刚才收集到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。

  1. Leader将最新的历史事务日志,同步给集群中的所有Follower;
  2. Follower处理事务,反馈给Leader;
  3. Leader接收到过半Follower的反馈信息后,向所有Follower发送Commit信息。Leader完成同步;
  4. Follower收到Leader的Commit消息,提交所有事务。Follower完成同步。
4.3.4 BROADCAST(广播)

完成同步阶段,就可以接收客户端新的事务请求,并进行广播。在正常运行中,Zookeeper会一直处于该阶段反复进行消息广播。如果Leader崩溃或其他原因导致Leader缺失,那么会重新进行选举,选举出新Leader。

  1. Leader接收客户端新的事务请求,生成事务并根据ZXID顺序,广播给所有Follower;
  2. Follower根据消息接受先后次序(由一个FIFO的队列维护)来处理Leader的事务,写入本地磁盘,写入成功后返回ACK给Leader;
  3. Leader收到过半的ACK之后,开始提交本事务,并广播事务提交信息;
  4. Follower接收Leader的Commit消息后,开始提交事务。

Zookeeper通过类二阶段提交来保证集群中数据的一致性,因为只需要收到过半的ACK就可以提交事务,所以Zookeeper的数据并不是强一致性。

ZAB在广播状态中保证了消息可靠传递,事务全局和因果有序:

  1. 服务器使用TCP协议进行通信,保证在网络传输的可靠性和有序性;
  2. 服务器维护一个FIFO的队列,保证全局有序性;
  3. 全局递增的ZXID,保证因果有序性。

5. 用途

5.1 数据发布/订阅(统一配置管理)

系统A、B、C共用的配置放在Zookeeper的某个ZNode上,再监听该ZNode,发生变更及时响应。

5.2 统一命名服务

给某个资源取个名字,然后放在Zookeeper上的某个ZNode上,通过名字获取资源。

5.3 分布式锁

创建一个持久节点当作锁,系统要抢锁,就要在该节点下创建临时顺序节点。

流程
假设创建的顺序为A、B、C,然后系统A、B、C会判断是否抢到了锁。

  1. 系统A:获取该节点下所有子节点,比较后发现自己的顺序最小,拿到锁。
  2. 系统B:获取该节点下所有子节点,比较后发现自己的顺序不是最小,就监听比自己靠前的第一个节点,即系统A创建的节点。
  3. 系统C:获取该节点下所有子节点,比较后发现自己的顺序不是最小,就监听比自己靠前的第一个节点,即系统B创建的节点。

系统A执行完后删除自己创建的节点,或系统A奔溃断开连接,节点自动删除。通过监听,系统B发现系统A创建的节点已删除,自己是最小的节点,于是就拿到锁。以此类推。

优点

  1. Zookeeper定位就是分布式协调,ZAB协议保证数据一致性。锁的模型健壮、简单易用,适合做分布式锁;
  2. 如果获取不到锁,只需要添加一个监听器就可以了,性能消耗较小。

缺点

  1. 性能没缓存服务高;
  2. 创建和删除节点只能在Leader服务器,然后将数据同步到所有Follower服务器上;
  3. 网络抖动导致连接断开,临时节点被删除;
  4. 如果有较多的客户端频繁的申请加锁、释放锁,对于集群的压力会比较大。

5.4 分布式队列

Znode的顺序节点天生支持先进先出,新创建的节点总是最大的,出队总是拿序号最小的节点即可。

参考:
从Paxos到Zookeeper 分布式一致性原理与实践
漫画:什么是ZooKeeper?
初识分布式协调框架ZooKeeper
面试官:说一下Zookeeper的ZAB协议?
CAP 定理的含义
万字带你入门Zookeeper
可靠分布式系统基础 Paxos 的直观解释
分布式系统协议Paxos、Raft和ZAB

漫画:如何用Zookeeper实现分布式锁?
分布式锁之Zookeeper
分布式锁用 Redis 还是 Zookeeper?
ZooKeeper 的应用场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值