2.2 集群模式:让三位法官同时开庭 本节摘要:集群部署的核心是 myid 与 server 列表的咬合、投票选出 Leader、以及奇数台机器背后的多数派算术。本节带你在本机或三台服务器上完成三节点部署,并亲手停掉 Leader 亲眼目睹一次换庭。 为什么是三台,而不是两台 先算一笔账再动手。ZooKeeper 的每次写入需要多数派(过半)确认,记总台数为 N,可容忍故障数为 N 除以 2 向下取整。三台容一(三台中挂一台,剩两台仍过半),五台容二,四台也只容一——四台的成本换来和三台一样的容错,这就是生产部署清一色奇数的全部原因。两台更尴尬:挂任何一台,剩下一台都凑不出多数派,可用性与单机无异,却多了一致性协议的开销。所以集群规模从三起步,按"要容几台故障"反推:容一台三台,容两台五台。
本节摘要:集群部署的核心是 myid 与 server 列表的咬合、投票选出 Leader、以及奇数台机器背后的多数派算术。本节带你在本机或三台服务器上完成三节点部署,并亲手停掉 Leader 亲眼目睹一次换庭。
先算一笔账再动手。ZooKeeper 的每次写入需要多数派(过半)确认,记总台数为 N,可容忍故障数为 N 除以 2 向下取整。三台容一(三台中挂一台,剩两台仍过半),五台容二,四台也只容一——四台的成本换来和三台一样的容错,这就是生产部署清一色奇数的全部原因。两台更尴尬:挂任何一台,剩下一台都凑不出多数派,可用性与单机无异,却多了一致性协议的开销。所以集群规模从三起步,按"要容几台故障"反推:容一台三台,容两台五台。
部署拓扑上还有两条军规。第一,机器分散在不同物理机、不同机架甚至不同交换机下——把三台部署在同一台宿主机的三个容器里,宿主机一抖三台全灭,多数派荡然无存。第二,主机名与端口提前规划好,集群配置里出现 IP 与主机名混用是排障时的经典噩梦。
本机模拟用三个目录、三个端口;真实环境把目录换成三台机器、端口统一为 2181,步骤完全一致。

每台机器的配置分三步:写 zoo.cfg、写 myid、逐台启动验证。
# 三台机器共用的 zoo.cfg(server 列表三台完全一致) tickTime=2000 initLimit=10 # 新节点连上 Leader 后,等多少个 tick 完成初始化同步 syncLimit=5 # 正常运行时,Leader 与 Follower 之间允许多少个 tick 的延迟 dataDir=/var/lib/zookeeper clientPort=2181 # server.编号=主机名:选举通信端口:提案广播端口 server.1=node1:2888:3888 server.2=node2:2888:3888 server.3=node3:2888:3888
# 每台机器各自写 myid——内容必须与 server.N 的 N 一致 $ echo 1 > /var/lib/zookeeper/myid # 在 node1 上执行 $ echo 2 > /var/lib/zookeeper/myid # 在 node2 上执行 $ echo 3 > /var/lib/zookeeper/myid # 在 node3 上执行 # 三台分别启动,然后逐台验证角色 $ bin/zkServer.sh start $ bin/zkServer.sh status Mode: leader # node1 输出(谁当选有随机性,两台 follower 即正常)
myid 与 server 列表的咬合关系值得单独强调:server.N 声明"编 N 的法官是谁",myid 声明"我是 N"。两者对不上,节点启动时会报 myid file is missing 或直接互不认识。2888 是 Leader 向 Follower 推送提案的端口,3888 是选举时互相拉票的端口,防火墙必须放行——三台之间 3888 不通时,你会看到日志里反复出现 Cannot open channel to 2 at election address,而集群永远选不出 Leader。
部署的意义在验证。把 Leader 停掉,看剩下的机器如何自我修复:
# 在当前 Leader(假设 node1)上执行 $ bin/zkServer.sh stop # 立刻到 node2、node3 上查看状态,几秒内完成重新选举 $ bin/zkServer.sh status Mode: leader # node2 或 node3 之一接任 Mode: follower # 客户端此时把连接切到幸存节点,读写照常 $ bin/zkCli.sh -server node3:2181 [zk] get /hello world # 数据未丢:多数派已持久化的提案依然在案
这次实验演示的正是第四章 4.2 节要展开的快速选举:幸存的两台交换投票、多数认可后立即上任,全程秒级。而如果你把三台停掉两台,会发现幸存的那台 status 报 Error contacting service——它不是坏了,是在拒绝服务,呼应 1.3 节的 CP 承诺。
问:能否加 Observer 凑数? 可以但别混淆——Observer 不参与投票,只是读扩展件,凑不进多数派。读流量大的集群用"三 Follower 加若干 Observer"扩展读能力,2.3 节的配置里会给出开关。
法庭搭好了,但配置文件里还有一串参数没读懂,下一节逐项过堂。
集群读流量大到三台 Follower 吃不消时,正确姿势不是加投票节点(多数派会变大、写路径变慢),而是加 Observer:它同步数据、服务读请求,但不参与选举与表决。配置两行即可:
# zoo.cfg 中追加 Observer 成员,并用分组声明其不投票 server.4=observer1:2888:3888:observer # 客户端照常连 Observer 的 clientPort,读请求本地应答 # 验证:state 输出为 observer
$ echo srvr | nc observer1 2181 | findstr Mode Mode: observer
Observer 的适用判据很清晰:读扩展需求真实存在且写流量不低时用它;纯粹"机器多显得可靠"而加 Observer 则毫无意义——它不在多数派里,对容错没有贡献。
生产交接前把这张单子过一遍,每一条都对应本章的一个坑位:myid 与 server.N 一一对应;三台之间 2888 与 3888 双向连通;机器分布在不同机架或宿主机;时钟同步服务在跑;tickTime 体系按机房延迟校准过;停一台验证换届、停两台验证罢工的实验都有记录。清单不通过不上线,这是 6.4 节运维体系的第一道闸。