跳转至

ZooKeeper 分布式协调服务核心概念

作者:JiangChong | 撰写时间:2026年10月

适用场景:任何需要理解「分布式选主、锁、配置中心」机制的读者;读 HDFS HA 篇之前建议先读本文建立 ZooKeeper 基础。零基础读者可先读 HDFS 架构原理 了解主从架构与单点故障的由来。

阅读背景(30 秒)

  • HDFS 架构原理 建立了「主从架构 + 单点管理员」的画面——分布式系统的共同问题是:谁来当那个「主」、挂了怎么换人、大家怎么达成一致;
  • 读完本文再回看 HDFS 高可用(QJM、ZKFC、Federation):ZKFC 抢锁、会话超时锁自动释放、watch 通知——这些能力的提供者就是本文的主角 ZooKeeper。

本文介绍 ZooKeeper 本身:它是什么、怎么工作、为什么大数据生态处处离不开它。

关联文章

理解全文脉络

本文按「为什么需要 → 数据模型 → 交互机制 → 集群原理 → 生态位置 → 运维」组织:第 1 节讲分布式协调难题,第 2、3 节讲 ZK 的两个核心抽象(znode 与 session/watch),第 4 节讲 ZK 自己如何做到高可用(Leader/Follower + 多数派——与 QJM 同源的思想),第 5 节给出大数据生态全景图,第 6 节落到部署运维。

新手建议顺序读;只关心「怎么用」的读者重点看第 2、3 节,准备部署的看第 6 节,赶时间的读每节末的「本节结论」。


1 为什么需要 ZooKeeper:分布式系统的「协调」难题

单机程序里,「谁当主」「锁给谁」「配置是什么」都是小事——一个内存变量就解决了。但放到分布式系统里,这些都变成了难题:

协调问题 单机做法 分布式下的困难
选主 进程内选一个线程 多台机器怎么投票?挂了一台怎么重选?怎么保证只选出一个?
锁 内存互斥量 锁放哪台机器?持锁者挂了锁怎么释放?
配置/成员发现 本地配置文件 配置改了怎么通知所有节点?某节点挂了大家怎么知道?

这三个问题有一个共性:需要一个所有节点都信任的「第三方」来仲裁。ZooKeeper 就是为此而生的通用服务——它自己不存业务数据,专门解决协调问题。

以「选主」为例,试想两台 NameNode 互发心跳,网络一抖,互相以为对方挂了,于是两个 NN 同时宣布自己是 Active——客户端向两边同时写数据,两份数据分道扬镳,这就是著名的脑裂(split-brain)。要避免它,就必须有一个客观的第三方来裁决「谁活着、谁当选」——这正是 ZK 的用武之地。

ZooKeeper 最初是 Yahoo! 为 Hadoop 生态开发的(名字源于「动物园管理员」——Hadoop 各组件都以动物命名,ZK 负责「管理动物们」)。2011 年从 Hadoop 项目独立出来,成为 Apache 顶级项目。

比喻:ZooKeeper 就像办公室中央的「公告栏 + 广播喇叭」。谁当选了值班长、谁还在上班、会议室钥匙在谁手里——都写在公告栏上,当选者的名牌也挂在上面;人一离开,名牌自动摘掉;公告栏有好几份、内容保持一致;内容一变,喇叭自动广播给关心的人。后面会看到,这个比喻几乎逐条对应 ZK 的机制:

办公室比喻 对应的 ZK 机制
公告栏 znode 树(第 2 节)
「值班长」的名牌 选主用的临时节点(第 2.1 节)
名牌随人走(人走自动摘) 会话超时 → 临时节点自动删除(第 3.1 节)
喇叭广播「内容变了」 watch 通知(第 3.2 节)
公告栏有多个一致副本 Leader 多数派复制(第 4 节)

本节结论:ZK 是分布式系统的「协调专用服务」——解决选主、锁、配置/成员发现三类问题,本身不存业务数据。


2 数据模型:像文件系统一样的 znode

ZooKeeper 的数据组织长得像文件系统:一棵树,路径就是地址。树上的每个节点叫 znode,它可以存一小段数据(配置、状态、锁的持有者信息等),也可以挂子节点。一棵典型的树长这样:

/                                  ← 根
├── /hadoop-ha                     ← 某个业务的命名空间
│   ├── ActiveStandbyElectorLock   ← 锁节点(ZKFC 抢的就是它)
│   └── ActiveBreadCrumb           ← 记录当前谁是 Active
└── /config                        ← 应用配置
    └── db.url = "jdbc:..."        ← 配置项就存在节点数据里

注意一个与文件系统的关键区别:znode 既像文件又像目录——同一个节点可以同时存数据、挂子节点,没有「文件」和「目录」之分。另外 ZK 的路径一律是绝对路径(从 / 开始),不支持相对路径和软链接。

2.1 四种 znode 类型

znode 按两个维度分型:持久/临时(会话断开后是否消失)、普通/顺序(名字是否带自增序号)。组合出四种:

类型 会话断开后 名字特点 典型用途
持久节点(PERSISTENT) 保留 固定 配置信息
临时节点(EPHEMERAL) 自动删除 固定 成员注册(「我活着」的打卡牌)、锁
持久顺序节点(PERSISTENT_SEQUENTIAL) 保留 带自增序号 任务队列
临时顺序节点(EPHEMERAL_SEQUENTIAL) 自动删除 带自增序号 分布式锁的排队

临时节点是 ZK 最精妙的设计:它的生命周期和客户端会话绑定——客户端活着,节点在;客户端断连(进程崩溃、网络中断),会话一超时,ZK 自动把这个节点删掉。这就是「人走茶凉」:HDFS HA 里 ZKFC 抢的那把锁就是临时节点,ZKFC 一挂,锁自动释放,另一个 ZKFC 才能顶上(见 HDFS 高可用(QJM、ZKFC、Federation) 第 4 节)。

以分布式锁为例走一遍完整流程:

  1. 客户端 A 创建临时节点 /locks/order → 成功,A 拿到锁;
  2. 客户端 B 也创建同名节点 → 失败(节点已存在),B 转而 watch 这个节点;
  3. A 干完活主动删除节点,或 A 进程崩溃、会话超时后 ZK 自动删除——两种方式殊途同归;
  4. B 收到节点消失的 watch 通知,立刻重新抢建 /locks/order。

无论 A 是正常释放还是「不辞而别」,锁都不会永久卡死——这就是临时节点的核心价值。

2.2 数据量的边界

znode 里能存的数据很小(默认上限约 1MB,以官方文档为准),设计上就是放协调元数据(谁在线、锁给谁、配置项),不是放业务数据。把 ZK 当数据库用(写入 GB 级数据、高频大对象)是典型的误用,会直接拖垮整个协调层。

2.3 版本号:天然的乐观锁

每个 znode 都自带三个版本号(数据版本、子节点列表版本、ACL 版本),每次写操作会让对应版本号 +1。写操作可以带上期望版本号:版本对不上就失败。比如两个客户端同时读到配置版本 5,A 先提交成功、版本变成 6;B 再提交时发现期望的 5 已经不是当前版本,被 ZK 拒绝——B 重读后再改。这就是乐观锁:冲突由「版本校验」兜底,多数场景下不必先拿分布式锁再改数据。

本节结论:znode = 树上的节点,存 KB 级协调元数据;临时节点「人走茶凉」是 ZK 实现锁和成员发现的基石;版本号让并发修改有了安全网。


3 Session 与 Watch:心跳打卡与变化通知

3.1 Session:客户端与 ZK 的「打卡」关系

客户端连上 ZK 后建立一个会话(session),之后定期发心跳维持。如果心跳中断超过 sessionTimeout(客户端和服务器协商的时长,通常秒级到几十秒),ZK 判定会话死亡:

  • 超时值是协商出来的:客户端提议一个值,服务器在 minSessionTimeout~maxSessionTimeout 范围内定夺(默认分别为 2 倍、20 倍 tickTime);
  • 客户端不用干等超时:间隔约 sessionTimeout/3 没有通信就主动发心跳;服务器这边,收到该会话的任何请求都会重置计时。

判定死亡后会发生两件事:

  1. 该会话创建的所有临时节点被自动删除(锁释放、成员注销);
  2. 关心这些节点的其他客户端会收到通知(见 3.2)。

这就是 HDFS 高可用(QJM、ZKFC、Federation) 里「锁与持有者心跳绑定」的底层机制——心跳一停,接力棒自动掉回赛道。

3.2 Watch:一次性的「门铃」

客户端可以给某个 znode 挂一个 watch(监视):节点数据变了、节点被删了、子节点增删了——ZK 会通知客户端一次。关键特性是 watch 是一次性的:

  • 通知只发一次,发完就失效;要继续监视,客户端收到通知后必须重新注册 watch;
  • 语义是「告诉你变了」,不保证「变了多少次」——事件之间发生了什么,客户端要自己再读一遍当前状态。

就像门铃按一次响一次:门铃不会告诉你「这一个小时里按了几次」,你听到铃声去门口看一眼现状即可。为什么设计成一次性?因为服务器不必为每个客户端维护永久的订阅状态——「通知一次 → 客户端主动重新注册」这个循环简单可靠,是 ZK 在超大规模集群里保持轻量的关键。

sequenceDiagram
    participant C as 客户端
    participant Z as ZooKeeper
    participant A as 管理员
    C->>Z: getData("/config", watch=true)
    Z-->>C: 返回当前配置
    A->>Z: setData("/config", 新值)
    Z-->>C: 通知:/config 变了
    Note over C: watch 已失效<br/>重读配置并重新注册 watch

(注:ZooKeeper 3.6+ 引入了持久 watch 可以自动反复触发,但「一次性 + 重注册」仍是主流用法,先掌握它再了解新特性更稳。)

3.3 两个机制合起来的威力

session(临时节点自动清理)+ watch(变化自动通知)= 分布式协调的最小完备组合:

  • 选主:大家抢建同一个临时节点,抢到的当选,其余 watch 这个节点——当选者失联,节点消失,watch 触发,其余人重新抢;
  • 成员发现:每个成员上线时创建自己的临时节点,管理者 watch 父节点——成员挂了节点自动消失,管理者立刻知道;
  • 配置下发:配置存在持久节点里,各节点 watch 它——配置一改,全员收到通知并重读。

以「选主」为例完整走一遍(就是 HDFS HA 里 ZKFC 抢锁的翻版):

  1. A 创建临时节点 /leader 成功 → A 当选;
  2. B、C 创建同名节点失败,watch /leader;
  3. A 失联 → 会话超时 → /leader 被 ZK 自动删除;
  4. B、C 同时收到 watch 通知,立刻抢建 /leader,先到先得,败者继续 watch。

全程没有「开协调会」的复杂协议,只有「创建 + 通知」两个动作——这就是 ZK 的优雅之处。

本节结论:session 保证「失联者自动出局」,watch 保证「变化自动广播」——两者叠加就是 HDFS HA 里 ZKFC 全部行为的底层原理。


4 ZK 集群:Leader/Follower 与 ZAB 多数派

如果 ZooKeeper 只跑一台,那它自己就成了新的单点故障——协调者比被协调者先挂,一切白搭。所以 ZK 自己也要集群化,而且要解决「多个 ZK 节点之间数据一致」的问题。

4.1 角色:Leader、Follower、Observer

角色 职责 备注
Leader 唯一处理写请求的节点;把变更广播给所有 Follower 选举产生,集群只有一个
Follower 参与投票(多数派计数)、可以服务读请求 与 Leader 保持数据同步
Observer 只服务读、不参与投票 可选角色(ZooKeeper 3.3+ 版本加入),用来横向扩展读能力,不拖慢多数派

所有写都经过 Leader:客户端写请求发给 Leader,Leader 把变更打包成「提议」广播给所有 Follower,多数派确认落盘后这次写才生效并返回客户端。这个协议叫 ZAB(ZooKeeper Atomic Broadcast,ZooKeeper 原子广播)。

一次写请求的完整旅程(对着 4.2 的图看):

  1. 客户端把写请求发给任意一台 ZK(哪怕连的是 Follower,也会被转发给 Leader);
  2. Leader 生成「提议」,连同全局递增的事务号 zxid 广播给所有 Follower;
  3. 每个 Follower 把提议写入本地事务日志(落盘),成功后回复确认;
  4. Leader 收到多数派确认后提交,各节点把变更应用到内存数据树,客户端收到「写成功」。

之所以强调「落盘」,是因为 ZK 靠已持久化的多数派副本保证「确认过的事绝不反悔」——哪怕这台机器立刻断电,数据也还在磁盘上。

在 HDFS 高可用(QJM、ZKFC、Federation) 第 3 节的 QJM:ZAB 的「多数派确认」和 JournalNode 的「多数派确认」是同一个思想——写操作要在超过半数的副本上落盘才算数,任何少数派的故障都不影响系统继续工作。分布式系统里,「多数派」是解决一致性的通用语言。

不过两者各管一层、互不冲突:ZAB 的多数派在 ZooKeeper 集群内部,管的是「谁当 Active」这个仲裁决定(ZKFC 抢锁);JournalNode 的多数派在 HDFS 侧,管的是 Active/Standby 两个 NN 之间的 edit log 数据复制。可以理解为「裁判」和「账本」的分工:裁判只裁定谁上场,账本只负责把账抄到多数派手里。实际部署中它们也是两套独立机器(ZK 集群 3 台、JournalNode 3 台),任何一边故障都不会拖垮另一边。

4.2 集群规模:为什么是奇数台

容忍 F 台故障,需要 2F+1 台节点(超过半数存活即可形成多数派):

节点数 多数派要求 可容忍故障
1 1 0(单点,仅学习用)
3 2 1
5 3 2

3 台与 4 台容错能力相同(都只容 1 台),所以奇数台是标准形态——这与 HDFS 的 JournalNode 数量选择完全同理。

实际演练:3 台集群挂 1 台,剩 2 台仍构成多数派,集群照常服务;再挂 1 台只剩 1 台,凑不出多数派,集群停止服务——所以 3 台只买得起「挂 1 台」的保险,要容忍 2 台故障得上 5 台。另外注意:Observer 不投票,数量不影响 2F+1 的计算,只影响读吞吐。

graph TB
    CL["客户端(选主/锁/配置)"] --> L["Leader<br/>唯一写入口"]
    L -->|"ZAB 广播,多数派确认"| F1["Follower ①"]
    L -->|"ZAB 广播,多数派确认"| F2["Follower ②"]
    L -.-> O["Observer(只读不投票)"]
    F1 -.-> CL
    F2 -.-> CL
    O -.-> CL

图注(读写路径):实线是写路径——客户端把写请求发给任意节点、最终汇聚到 Leader,Leader 经 ZAB 广播给 Follower,多数派确认后才生效;虚线是读路径——客户端可直接从 Follower 或 Observer 读(Observer 不参与投票,仅服务读);此外 Leader 把提交流同步给 Observer(图中 L → O 虚线),保持其数据最新。

4.3 一致性的边界:读可能「旧一秒」

ZK 的一致性有一个重要边界:写是强一致的(多数派确认后才生效),但读可能读到旧数据——如果读请求落在还没追上 Leader 的 Follower 上,可能读到稍旧的值(ZK 保证的是顺序一致性:客户端看到的顺序不乱,但不保证所有客户端同一时刻看到同一份数据)。

对「选主/锁」这类协调场景,这个边界足够且是刻意为之(换来了读的可扩展性);对需要强一致读的场景,客户端可以用 sync 操作强制追平。理解这个边界,能避免很多「ZK 数据怎么不一致」的困惑。

举个例子:锁刚刚释放,连在 Follower ① 上的客户端 A 读 /lock 可能还看到「锁被占着」,而连在 Leader 上的 B 已经看到「锁已释放」。ZK 保证的是顺序不乱:A 现在看到旧值,接下来必定会看到新值,绝不会「一会儿新一会儿旧」。真要严格,A 可以先发一个 sync 再读——sync 强制这台服务器先追上 Leader,之后 A 的读就是最新数据。

那么滞后到底有多大?正常情况毫秒级:Leader 提交后,COMMIT 消息发到 Follower、Follower 按序应用到内存数据树,通常只差一两个网络往返加排队时间(个位数到几十毫秒);写压力大或 Follower 磁盘慢时可能到秒级。注意两点:① ZK 设计上不承诺滞后的时间上界;② 但有实际兜底——Follower 掉队太远(超过 syncLimit,默认 5 × tickTime = 10 秒无响应)会被踢出集群,重新同步期间不服务读,追平后才重新接客。所以「正在服务读的 Follower」要么几乎最新,要么干脆不服务,不会无限旧下去。

本节结论:ZK 用 Leader + ZAB 多数派保证自身高可用与写一致;读可能短暂滞后,是顺序一致性而非强一致。集群规模 2F+1,3 台起。


5 ZooKeeper 在大数据生态的位置

ZooKeeper 被叫做「大数据生态的粘合剂」,因为它几乎被每个组件当协调层用:

graph TB
    ZK["ZooKeeper 集群"]
    ZK --- A["HDFS HA<br/>选主(ZKFC 抢锁)"]
    ZK --- B["Kafka<br/>元数据 / 消费者组"]
    ZK --- C["HBase<br/>Region 分配 / 元数据"]
    ZK --- D["Flink HA<br/>JobManager 选主"]
组件 用 ZK 干什么 说明
HDFS HA ZKFC 抢锁选主 跨系列应用,见 HDFS 高可用(QJM、ZKFC、Federation)
Kafka Broker 注册、Controller 选举、消费者组管理 注意:Kafka 2.8+ 引入 KRaft 模式逐步移除 ZK 依赖,新版本可无 ZK 运行
HBase RegionServer 注册、Region 分配状态、元数据表位置 老牌 ZK 用户
Flink JobManager HA 选主、检查点协调 高可用模式必配

为什么大家都复用 ZK 而不是各写各的协调逻辑?因为协调问题看似简单、做对极难——要正确处理网络分区、进程崩溃和时序问题,一个久经考验的组件远比自研划算;而 ZK 出自 Hadoop 生态、足够成熟,Kafka/HBase/Flink 这些后来者自然顺势采用。

反过来,也有一批系统不需要 ZK,最典型的是 Vertica 这类无主对称架构的 MPP 数据库——它们把协调做进了产品内部:

  • 不需要选主:所有节点逻辑对称,谁都能发起查询,没有「主节点」需要仲裁;
  • 成员发现靠 Spread:集群内置通信层(4803 端口)负责心跳与成员管理,等于内置了一个 ZK;
  • 元数据用 catalog 全同步:每个节点持完整元数据副本,任何 DDL 要求全部节点确认——比 ZK 的多数派更强,代价是任一节点故障都会拖住元数据变更。

选型规律一句话:主从架构、需要选主的系统找外部协调服务(Hadoop 系找 ZK,K8s 找 etcd);无主对称架构的系统自建协调(Vertica 的 Spread)。 Greenplum 是第三种路线:Master/Segment 有主架构,但不搞自动选主——standby 用 WAL(Write-Ahead Logging)流复制兜底、健康检查靠 FTS 探测,故障切换需人工介入,所以它既不用 ZK 也不用 Spread。三种路线各有代价:外部仲裁要多运维一套组件;内置协调(Spread)与数据库同生共死;Greenplum 路线最省组件,但牺牲了自动故障切换。

正因为 ZK 被这么多人依赖,它挂掉的影响面极大:HDFS HA 失去自动切换、Kafka(KRaft 前)Controller 选举停滞、HBase Region 分配冻结、Flink JobManager 无法选主——所有依赖方同时「失明」。这也是第 6 节运维要点里「ZK 必须独立部署、独立监控」的原因。

本节结论:ZK 是大数据生态的通用协调层;学一次 ZK,HDFS/Kafka/HBase/Flink 的 HA 机制全部复用。


6 部署与运维要点

  1. 集群规模:生产 3 台或 5 台(2F+1),独立于业务节点部署——ZK 是仲裁层,和业务节点同机等于让一次硬件故障同时打击「裁判」和「选手」。
  2. 必配项:dataDir(数据目录,持久化磁盘)和该目录下的 myid 文件(每台机器写自己的编号 1/2/3,与配置文件里的 server.N 对应)——漏配 myid 是新手最常见的起不来原因。
  3. 端口:客户端 2181、集群间通信 2888、选举 3888。
  4. 监控:进程存活 + srvr/ruok 等四字命令输出(四字命令:srvr、stat、ruok、mntr 这类四个字母组成的运维查询命令;新版本需先在 4lw.commands.whitelist 里放行)、会话数、延迟、磁盘使用率——ZK 静默挂掉是重大事故的常见源头。
  5. 别存大东西:znode 数据量保持在 KB 级,定期清理僵尸节点;ZK 数据目录要单独磁盘,避免被其他进程挤爆 IO。
  6. 别用 ZK 存业务数据:ZK 是协调层,业务数据请交给 HDFS/Kafka/数据库。

把第 2、3 条串起来看,zoo.cfg 里集群的定义长这样(每台机器上的 myid 文件内容对应 server.N 的编号 N):

server.1=zk-node-1:2888:3888
server.2=zk-node-2:2888:3888
server.3=zk-node-3:2888:3888

本节结论:ZK 运维的核心是「独立、奇数台、小数据、盯监控」——它必须比它协调的系统更可靠。


最佳实践清单

  1. ZK 集群 3 台起、奇数台,独立部署 —— 为什么:2F+1 才能容忍故障;与业务同机则仲裁层随业务故障一起失效。
  2. 每台机器配好 myid 并放在 dataDir 下 —— 为什么:ZK 靠 myid 对应 server.N 配置,漏配直接起不来。
  3. 只存协调元数据,znode 数据保持 KB 级 —— 为什么:ZK 为协调设计,大数据量会拖垮写吞吐与集群健康。
  4. watch 触发后重新注册,读数据时以当前状态为准 —— 为什么:watch 一次性、只通知「变了」,不保证中间变化次数。
  5. 需要强一致读时用 sync —— 为什么:Follower 读可能滞后,顺序一致性 ≠ 强一致。
  6. ZK 纳入监控:进程、会话数、延迟、四字命令 —— 为什么:ZK 静默失效会让所有依赖方逐个出问题,且它自己「不报错」。
  7. Kafka 新集群评估 KRaft 模式 —— 为什么:Kafka 2.8+ 逐步去 ZK,新部署可少维护一套依赖(存量 ZK 型集群不受影响)。
  8. 数据目录独立磁盘 + 定期清理僵尸节点 —— 为什么:ZK 数据目录 IO 抖动直接影响心跳与选举,僵尸节点堆积浪费内存。
  9. 会话超时按网络环境配置,不要一味调大 —— 为什么:超时太长 = 真故障发现慢、锁释放慢;太短 = 网络抖动就频繁误判失联。
  10. 升级 ZK 时滚动进行,保证多数派在线 —— 为什么:同 HDFS JournalNode 的升级原则,同一时刻最多停 (N-1)/2 台。

附录:新手常见疑问(FAQ)

Q1:ZooKeeper 是数据库吗?能存业务数据吗? 不是。它是协调服务,znode 默认上限约 1MB 且全内存+快照工作——存业务数据既慢又危险(第 2.2 节)。业务数据请交给 HDFS/Kafka/真正的数据库。

Q2:为什么 watch 只通知一次? 「通知一次 → 客户端重读状态并重新注册」的循环让服务器不用维护海量永久订阅,简单可靠(第 3.2 节)。代价是中间变化会漏掉——所以收到通知后要读当前状态而不是数事件。

Q3:ZK 集群为什么必须奇数台? 2F+1:4 台和 3 台的容错能力一样(都只容 1 台故障),偶数台纯属浪费。这与 HDFS JournalNode 的数量选择是同一个多数派原理(第 4.2 节)。

Q4:ZK 挂了,HDFS HA 会怎样? 两个 NN 的 ZKFC 都连不上 ZK → 选举失灵、自动故障转移停止。已在线的 NameNode 服务不受影响,但故障切换会失效——所以 ZK 要独立部署、重点监控(第 5、6 节)。

Q5:生产能用单台 ZK 吗? 不能。单台 ZK 自己就是单点,协调者比被协调者先挂,HA 形同虚设。学习环境可以,生产最低 3 台(第 6 节)。

Q6:为什么有时候读到旧数据? 读可能落在还没追平 Leader 的 Follower 上(顺序一致性,非强一致)。协调场景够用;需要强一致读时用 sync(第 4.3 节)。

Q7:ZooKeeper 和 etcd 什么关系? 同类竞品:都是分布式协调服务。etcd 是 K8s 的事实标准(用 Raft 协议),ZK 是大数据生态的事实标准(用 ZAB-ZooKeeper Atomic Broadcast)。选型一般跟着生态走:大数据圈用 ZK,K8s 圈用 etcd。

Q8:为什么 ZooKeeper 名字这么奇怪? Yahoo! 开发它时,Hadoop 组件都用动物命名(Hadoop 是一头玩具象的名字,Pig、Hive、Oozie……),「ZooKeeper(动物园管理员)」就是管理这些动物们的角色——职责如其名:协调。

Q9:客户端应该连哪台 ZK?需要知道谁是 Leader 吗? 不需要。客户端只需配好集群地址列表(连接串),连上任意一台即可:写请求会被自动转发给 Leader,读请求由所连节点本地处理(第 4.1 节)。

Q10:ZK 集群会不会出现两个 Leader(脑裂)? 不会。网络分区把集群劈成两半时,只有持多数派的那一半能选出新 Leader;少数派一方凑不够票,原 Leader 也失去多数派支持、无法再提交任何写。多数派机制同时防住了脑裂和数据分裂(第 4 节)。

Q11:Leader 是怎么选出来的? 用快速选举协议(Fast Leader Election):先比 zxid(谁的数据更新,谁优先),zxid 相同再比 server id(编号大者胜)。得票过半即当选(第 4.1 节)。


扩展阅读