0%

ZK 的主机选举与数据同步

集群角色

Leader
  • 集群内只有一个 Leader,负责调度处理事务请求,保证事务顺序性
  • 和 Follower 一样可以处理客户端的读请求
  • Leader 也是集群内部服务的调度者
Follower
  • 处理客户端的读请求;事务请求会转发给 Leader 处理
  • 参与投票,包括事务 Proposal、Leader 选举
Observer(3.3.0+)
  • 处理客户端的读请求;事务请求会转发给 Leader 处理
  • 不参与任何投票,无论是选举还是事务写过半成功;也会不作为 Leader 候选者
  • 因为不会成为 Leader, 所以不需要对数据进行持久化,故障恢复时会从 Leader 同步整个命名空间
  • 添加 Observer 可以在不降低事务能力的同时增加读请求的吞吐量

ZAB 协议

ZK 保证各服务端同步的核心是他的 Zookeeper Atomic Broadcast 协议。ZAB 被使用在 ZK 的选举过程和同步过程中。

主机选举
  1. Server 进入 Looking 状态,每个 Server 发出投票给其他通信的 Server,主要内容包含
    • id(sid) 被推举的 Server 标识,初始情况下是本机 sid
    • zxid 被推举的 Server 的 zxid,初始情况下是本机的 zxid
    • electionEpoch 选举纪元,判断收到的投票是否是本轮选票
  2. Server 接收他人投票,验证本机状态和选票有效性
  3. 处理投票,优先选择 zxid 大者,其次选择 sid 大者。如果投票被更新,则再次广播
  4. 统计投票,一单某 Server 得票超过一半,则被视为 Leader,对应 Server 从 Looking 状态变更为 Leading,其他从 Looking 变更为 Following
恢复阶段
  1. Leader 为每个 Follower 准备一个 LearnerHandler 线程服务
  2. Follower 向 Leader 发送自己的信息,其中包含 peerEpoch
  3. Leader 接受 Follower 信息,选择最大 peerEpoch + 1 确定为本届的 epoch,将 epoch 与本机信息发送给 Follower
  4. Follower 接受新的 peerEpoch,并组合新的 zxid 发送给 Leader
  5. Leader 接受 Follower 的 zxid,与自己相比较来确定数据差异,进行数据同步
    • 如果两者 zxid 一致,说明同步完成。否则判断 Follower 的 zxid 是否在 minCommittedLog 与 maxCommittedLog 之间
    • 如果 Follower zxid 小于 minCommittedLog,直接让 Follower 采用快照恢复
    • 如果 Follower zxid 大于 maxCommittedLog,此时取信与 Leader,删除 Follower 超出的事务日志
    • 如果 Follower zxid 介于中间,Leader 会传输 zxid 至 maxCommittedLog 之间的事务
数据广播

Server 端的几个重要数据结构:

  • long lastProcessedZxid 记录最新的事务编号
  • LinkedList<Proposal> committedLog 最近一段时间内的提案队列,默认大小是 500
  • long minCommittedLog 提案队列中最小的事务编号
  • long maxCommittedLog 提案队列中最大的事务编号
  • ConcurrentMap<Long, Proposal> outstandingProposals 保存等待表决的提案,Leader 独占的数据结构
  • ConcurrentLinkedQueue<Proposal> toBeApplied 保存准备提交的提案,Leader 独占的数据结构

事务生效的过程核心在于 Leader

  1. Leader 接受到客户端写请求,创建提案,保存在 outstandingProposals 当中
  2. Leader 向 Follower 广播提案,如果获得半数以上通过,则从 outstandingProposals 中删除,转移到 toBeApplied
  3. Leader 向 Follower 广播提交,同时自己也开始提交过程。数据保存进内存树,更新 lastProcessedZxid 以及 Log 相关数据。这一步可以更具体一点:
    1. 先向 Follower 进行广播,通知提交
    2. 主机进行刷盘,每个请求都会被刷盘,毕竟这样才会保证数据不被丢失。但是刷盘的动作每次触发显然很影响效率,这里会有一个间隔,批量地刷盘,以增加部分请求的延迟为代价提升整体的吞吐量
    3. 等待足够的 Follower 回复
    4. 进行下一步响应给客户端
  4. Leader 响应客户端并将之前的提案从 toBeApplied 删除。
谢谢支持!