想象你是一栋 30 层写字楼的物业经理。楼里入驻了几十家公司(微服务),每家公司都需要向物业中心查水电工电话(服务发现)、预约会议室(分布式锁)、了解业委会主任是谁(领导者选举)、查看公告栏的最新通知(配置管理)。一旦物业中心自己先乱了套,全楼都会停摆——这就是分布式系统中“协调服务”存在的意义。 现实世界里,最出名的两套“物业中心系统”就是 ZooKeeper 和 Etcd。ZooKeeper 像一家老牌行政中心:档案按部门、项目、服务逐层锁进档案柜(树形数据模型),所有文件由主任按严格顺序签发(ZAB 协议),还有成熟的临时登记牌和排队号制度。Etcd 则像一家新式政务大厅:资料全部录入智能平板(扁平键值存储),采用“轮值班长”制度(Raft 共识协议),通知通过群聊实时推送,而不是靠保安跑腿喊话。两套系统都能协调全楼秩序,但办事风格、适用场景截然不同。 本篇将把 ZooKeeper 和 Etcd 放到同一张工作台上,从核心职责、数据模型、一致性协议、核心机制、API 设计、性能特点、典型场景与选型决策八个维度逐一拆解对比,帮你建立直觉,选出最适合自己系统的“物业中心”。
在云原生与大规模分布式系统建设中,协调服务就是系统的“地基”。掌握 ZooKeeper 与 Etcd 的区别和选型逻辑,能有效避免架构踩坑,是后端与基础架构工程师的核心竞争力。
可视化演示
展示ZK树形与Etcd KV数据模型、ZAB与Raft共识协议流程及性能指标对比,交互切换理解两大协调服务差异。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+modeData.model.caption+'
核心职责
写字楼的物业中心,首要职责是“让全楼只认一个公告栏”。无论哪家公司要发布新装修通知、更换安全负责人,都得先到物业中心登记,再由中心统一广播。分布式系统也一样:几十上百个服务节点运行在不同机器上,它们需要共享一份“全网公认的事实”——谁还活着、哪个节点是主、配置变成什么了。这两套协调服务的核心职责,就是提供这个唯一可信的中央协调点。
ZooKeeper 和 Etcd 都属于分布式协调服务,它们在架构上有一个共同点:集群由多个节点组成,通过“多数派投票”来决定事务是否成功。为什么需要多数派?因为任何一次重大决策(比如选主、提交配置变更),只要有超过一半的节点确认,即使部分节点宕机,系统依然知道自己没有“分叉”。这种“少数服从多数,宁缺毋滥”的机制,保证了协调结果在全网范围内强一致。
而“多数”到底是多少?设集群总节点数为被 $n$,法定人数为 $q$,那么必须满足 $q > n/2$,即严格超过一半。
回到比喻:老城区政务中心和新城区政务中心虽然办事风格不同,但都有一个硬规矩——办成任何大事,必须让过半数的窗口都签字确认。这就是协调服务的第一性原理:用多数派共识换取全网统一的秩序。
- n(节点总数)集群中参与投票的服务器数量。假设你部署了 5 台协调服务器,那么 $n = 5$。
- ⌊n/2⌋(向下取整)计算机计算“一半”之后向下取整。5 台机器的一半是 2.5,向下取整得到 2。
- +1(严格多数)在取整结果上加一,确保是“超过一半”而不是“恰好一半”,避免投票打平。
数据模型
走进老城区政务中心的档案室,你会看到一排排档案柜:第一层是“部门”,第二层是“项目组”,第三层才是具体“服务实例”。要找一份材料,必须先知道它在哪个柜、哪一格、第几层,这就是 ZooKeeper 的树形数据模型。而新城区政务中心的办事员手里只有一台平板:所有材料都拍平了存在一张大表里,每份材料有一个唯一编号,直接搜编号就能拿到,这就是 Etcd 的扁平键值模型。
具体来说,ZooKeeper 把数据组织成一棵“目录树”,树上的每个节点叫 znode,路径形如 /app/cart/instance1。它就像文件系统:一层套一层,天然表达“归属关系”。znode 还分为持久节点(文件一直存在)、临时节点(会话结束自动消失)、顺序节点(自动编号)等类型。Etcd 则放弃层级,把所有数据都当成扁平的 key-value:key = "app/cart/instance1",value 是任意字节串。这个 key 看起来像路径,但只是字符串,没有父子关系,也不存在“删了父节点连带删子节点”的级联效应。
数据模型决定了两个系统的“操作心智”:
老中心的档案系统层级分明、谁属于谁一目了然,适合表达复杂归属;新中心的平板则简单直接、没有“剪不断理还乱”的父子牵绊,适合海量扁平数据的快速读写。树与表,是这两套系统给人的第一印象。
- P_tree(树形路径)ZooKeeper 的访问方式是从根
/出发,沿层级逐段定位,路径有父子关系。 - K_kv(扁平键)Etcd 的 key 是一条完整字符串,斜杠只是普通字符,不表达层级。
- 双向箭头(语义对应)两者虽然长得像,但左边需要逐层遍历,右边可以直接寻址;删除左边
/a会连带删除/a/b,删除右边"a/b/c"不会影响任何其他键。
一致性协议
老城区政务中心有一套“主任签发制”:所有的重要文件必须由主任先起草(proposal),然后按名单顺序逐一发给各位窗口负责人确认,收到超过一半人的回执后,主任才会正式盖章发布。这个过程就叫 ZAB(ZooKeeper Atomic Broadcast)协议。它的核心特征是“一个 Leader 主导 + 全局严格的顺序号”:所有写操作都要经过 Leader,并且每个写请求都带一个全局递增的事务编号 zxid,谁编号大谁就更新。
新城区政务中心则采用“轮值班长制”,这就是 Raft 协议。系统里所有节点都有资格当班长,但每个“任期”(term)只能有一个班长。想当班长要先拉票,获得超过半数支持后上任;上任后所有日志都通过“复制到多数派”来确认。Raft 的排序规则同样依赖一个全局递增的“任期号 + 日志序号”,这与 ZAB 异曲同工。两者都能保证线性一致性(Linearizability):系统对客户端呈现的效果,就像只有一个单一副本在按顺序执行所有操作。
ZAB 用于区分的核心令牌是 zxid,它由两段拼接而成:
回到比喻:老中心的“主任签发制”让文件永远只有一个来源、一个顺序,严谨但更换主任时的交接流程繁琐;新中心的“轮值班长制”选举更灵活,同样保证任何时刻只有一个人说了算。两种制度殊途同归,都保证了“楼里永远只有一份权威公告”。
- epoch(领导纪元)每换一届 Leader,epoch 就加一。它就像一个“任期编号”,用来区分不同领导任期内的文件,防止旧领导的残留文件干扰新秩序。
- counter(事务计数器)同一届 Leader 任期内,每处理一个写事务就递增一次。它记录“这一届已经发了多少号文件”。
- 组合排序比较两个 zxid 时先看 epoch,epoch 大者一定更新;epoch 相同则看 counter,counter 大者更新。这样整个集群就有了一个全局唯一的“先后顺序”。
核心机制
老城区政务中心有几件招牌工具,新城区也各有对应。第一件是“临时登记牌”:来政务中心办事的临时人员先领一块牌子,人一走牌子立刻作废。ZooKeeper 的临时节点(Ephemeral Node)就是这块牌子——它绑定客户端会话,会话断开节点自动消失,用来记录“谁还活着”再好不过。第二件是“排队号”:大家先取号,按顺序叫号办理。ZooKeeper 的顺序节点(Sequential Node)会自动附加一个单调递增的序号,谁序号小谁先办,天然支持公平的分布式锁。
新城区政务中心的对应物是“电子租约”(Lease):所有事物都是“租”来的,租期到了必须续费,不续费就自动释放。Etcd 的租约机制比 ZooKeeper 的临时节点更灵活——一个租约可以绑定多个 key,续约一次全部延期。而通知方式差异更大:ZooKeeper 的 Watch 像“门卫大叔喊一嗓子”——数据一变,门卫通知你一次,之后你再想被通知就得重新跟门卫登记;Etcd 的 Watch 像“群里订阅推送”——只要不取消订阅,每次变更都会一直推给你,中途不会断流。
临时节点的生命期与客户端会话严格绑定,可以用下面这个等号表达:
从“一次性门卫通知”到“持续订阅推送”,从“临时登记牌”到“可批量续租的电子租约”,两种机制的设计哲学一目了然:老中心偏保守稳定,新中心更灵活高效。
- life(znode_ephemeral)临时节点在目录中存在的时间。它不是一个永久的文件,而是“活期账户”,随会话的生死而生死。
- life(session)客户端与集群之间保持连接的存活时间。只要客户端不断开、没有超过超时时间,会话就一直有效。
- 等号(同生共死)临时节点不会比它的会话活得更久。利用这个特性,服务实例宕机后,注册在 ZooKeeper 上的临时节点就会自动消失,其他节点立刻感知。
API设计
如果你去老城区政务中心办事,会发现每个窗口只干一件事:领表窗口、盖章窗口、查询窗口……ZooKeeper 的 API 也是这样高度分化的:create 建节点、delete 删节点、exists 查存在、getData 读内容、setData 写内容、getChildren 列子节点。每个操作语义单一,开发者需要像指挥官一样把多个命令串起来完成复杂逻辑。
新城区政务中心则只有一个“综合终端”:Etcd v3 提供了统一的 gRPC 接口,核心操作就四个——Put(写入)、Get(读取)、Delete(删除)、Watch(监听),外加一个强大的 Txn(事务)。这个 Txn 接口特别像“一单式办理”:你可以一次提交多个条件判断和多个操作,要么全部成功,要么全部失败。其中最经典的就是 CAS(Compare-And-Swap):先检查版本的当前值,匹配了才写入。
CAS 的语义可以写成一条条件规则:
老中心的“专用窗口”命令虽多,但每个都很直观;新中心的“综合终端”命令虽少,但借助事务能力能表达更复杂的组合逻辑。如果你习惯用 SQL 里的 UPDATE ... WHERE version = ?,那么 Etcd 的 Txn 会让你倍感亲切。
- version(k)(版本号检查)读取键 k 当前的修订版本号。Etcd 中每个 key 维护一个版本号,每次修改都会递增。
- 条件判断将读到的版本号与期望值 v0 比较。相等说明这期间没人动过它,你有权修改;不相等说明有人抢先改了,你的操作应该失败。
- then put(k, v1)条件满足时执行写入,把值更新为 v1。整个过程在事务内原子完成,不会出现“检查完又被别人插队”的竞态。
性能对比
老城区政务中心办一次盖章要过三关:提交申请、主任审批、窗口确认,每个环节都要存档留痕,所以“写”很慢;但查询窗口散布各处,大家同时查也不会打架,所以“读”相对轻松。ZooKeeper 正是如此:所有写请求必须由 Leader 广播给多数派并落盘,一次写至少要等一次磁盘 fsync 和一轮网络往返;而读请求可以由任意 Follower 直接响应,天然做了负载均衡。
新城区政务中心则上了电子化系统:Etcd 将多个写请求打包成一沓,批量写入磁盘,提交效率更高。它的写入性能通常优于 ZooKeeper,这也解释了为什么 Kubernetes 这种超大规模集群敢于把 Etcd 当成唯一的数据存储。但 Etcd 的读路径需要经过 Raft 层的校验,大规模读压力下往往要配合缓存或 Watch 缓存来缓解。
一次写事务从发起到提交,延迟大致由两部分构成:多数派中落盘最慢的节点耗时,加上一轮网络往返:
在实际运维中,Etcd 依靠批量提交获得了更好的写吞吐,但频繁的磁盘抖动和网络延迟同样会拖垮它;ZooKeeper 写性能天花板较低,但它的读路径分散和成熟的 Watch 机制让它在传统业务中依然能打。性能没有绝对的胜负,只有是否匹配你的压力模型。
- t_fsync(落盘耗时)每个节点把日志写入磁盘所需的时间。磁盘越快,这步越短。
- max over Q(最慢的多数派)Q 是法定多数,取集合中最大的落盘耗时。Leader 必须等多数派都落盘,所以最慢的那台机器决定了整体延迟。
- t_RTT(网络往返)Leader 与 Follower 之间一次请求-确认的往返时间。消息在网络上的时间无法省略。
典型场景
两个政务中心都能办四件大事,只是手法不同。第一件:配置管理。ZooKeeper 把配置放在树形目录里,适合“按服务分组”的配置;Etcd 则凭借流式 Watch 让配置一改、全网秒级生效,所以 Kubernetes 的 ConfigMap 用 Etcd 存储。第二件:服务发现。ZooKeeper 的临时节点天然适合做注册中心:服务启动时注册一个临时节点,宕机时节点自动消失,Dubbo、Kafka 等老牌框架都这么干;Etcd 在云原生场景同样可以,但需要自己借助 Lease 模拟“临时节点”。
第三件:分布式锁。ZooKeeper 的经典做法是“排队号锁”:多个客户端同时在同一个锁目录下创建临时顺序节点,谁序号最小谁就拿到锁;Etcd 则用事务 + 租约:通过 CAS 尝试写入锁 key,设置租约防止持锁者崩溃后锁不被释放。第四件:领导者选举。两者的思路完全一致——竞选者去某个路径下创建节点,谁成功谁就当 Leader。
排队号锁的判定规则,可以浓缩成一个条件式:
回到比喻:老中心靠“排队号”维护公平,新中心靠“抢单 + 租约”保证容错。无论哪种方式,四件差事——配置、发现、锁、选举——都能妥妥办妥,只是你需要知道在哪扇窗口办。
- seq_i(自己的序号)每个竞争者创建顺序节点后获得一个递增序号,队伍里的人都有号。
- min{seq_j}(当前最小号)等待集合中所有序号的最小值,也就是队伍最前面的那个人。
- iff(当且仅当)自己的序号恰好等于最小号时,锁才算获取成功;否则继续等待并监听前一个节点释放。
选型决策
最后的问题:这栋大楼到底该选哪家物业中心?答案取决于你楼里已经住了哪些公司。如果你的技术栈是 Hadoop、HBase、Kafka、Dubbo,这些组件很多已经把 ZooKeeper 写死在配置里,强行换 Etcd 等于连地基一起拆;反过来,如果你的主线是 Kubernetes、云原生、微服务网格,Etcd 就是绕不开的选择,它早已是 k8s 生态的标准组件。
选型时还要考虑一致性体验和运维成本。ZooKeeper 和 Etcd 都能提供强一致性,但 ZAB 的顺序广播对传统“通知—确认”模式更友好,Raft 的日志复制和批量提交则更适合高吞吐的现代化系统。运维上,ZooKeeper 历史悠久、资料多但配置项也多;Etcd 依靠 bolt 存储和内置的 etcdctl 工具,上手门槛普遍更低。规模化之后,Etcd 要特别注意磁盘性能,ZooKeeper 则要留意连接数上限。
如果把选型看作一个多目标决策,可以给每个候选方案打分再求和:
老牌物业资历深、兼容老系统;新锐物业现代化、拥抱云原生。两者没有绝对的优劣,只有“适不适合你当前的楼”。把生态、性能、运维、团队熟悉度放到决策表里过一遍,答案自然浮现。
- score_{i,j}(方案评分)第 i 个候选方案(ZooKeeper 或 Etcd)在第 j 个维度(生态、性能、运维等)的打分。
- w_j(指标权重)第 j 个维度的重要程度。比如你特别在意云原生,就把“云原生适配”的权重调高。
- argmax(取总分最大者)对所有候选方案计算加权总分,选出分数最高的那个。
AI 应用场景
模型服务注册与版本管理
阶段:模型服务化部署与版本发布
解决的问题:在 AI 推理集群中,模型服务实例经常上下线、版本频繁迭代。传统做法靠人工维护 IP 清单,扩容缩容时很容易漏改、错改。利用 Etcd 的扁平 KV 和流式 Watch,每个模型服务启动时向注册中心写入自己的地址与版本号,下线时通过租约自动清理;调用方通过 Watch 持续感知实例变化,实现无感知的服务发现和灰度切换。
具体案例:某推荐系统团队把数百个模型推理实例全部注册到 Etcd,特征平台通过 Watch 实时更新下游路由表,模型版本从 v12 升级到 v13 时无需重启任何网关。
分布式训练主节点选举
阶段:分布式训练任务的协调与容错
解决的问题:在大规模分布式训练中,需要从多个工作节点中选出一个“首席节点”负责全局梯度聚合和任务调度。如果主节点宕机,系统要快速感知并选出新主。利用 ZooKeeper 的临时节点和顺序节点,所有训练节点在协调目录下竞选,序号最小者当选;主节点会话断开时临时节点自动消失,其余节点收到 Watch 通知后立刻发起新一轮竞选。
具体案例:某深度学习平台用 ZooKeeper 管理海量训练任务的领导节点,单任务主节点故障恢复时间从分钟级压缩到秒级,训练中断损失大幅降低。
A/B 实验配置与特征开关管理
阶段:在线推理与实验调优
解决的问题:推荐系统每天要调整成百上千个特征开关和 A/B 实验参数,传统做法是改配置重启服务,代价高昂。借助 Etcd 的 Txn 事务与流式 Watch,配置中心把每条实验配置作为独立 key,支持带版本地原子更新;各推理节点订阅前缀,配置一变立即热加载,秒级灰度生效且不会读到中间状态。
具体案例:某电商推荐场景中,运营团队通过 Etcd 配置中心同时调控 200 多个实验开关,上线新策略不再需要发版,实验流量随时可调。
Python 演示
# -*- coding: utf-8 -*-"""模拟 ZooKeeper 风格与 Etcd 风格的领导者选举流程""" class ZKStyleElection: """模拟 ZooKeeper 顺序节点小号优先 + 临时节点自动消失""" def __init__(self): self.sequence = 0 self.candidates = {} def join(self, node_id): """节点加入竞选, 模拟创建临时顺序节点并拿到序号""" self.sequence += 1 self.candidates[node_id] = self.sequence return self.sequence def get_leader(self): """选举结果: 序号最小的节点当选""" if not self.candidates: return None return min(self.candidates, key=lambda n: self.candidates[n]) def leave(self, node_id): """节点退出或会话断开, 临时节点自动消失""" self.candidates.pop(node_id, None) class EtcdStyleElection: """模拟 Etcd 抢锁式选主 + 租约续期""" def __init__(self): self.revision = 0 self.term = 0 self.cur_leader = None def try_campaign(self, node_id): """模拟事务 CAS: 没有 Leader 时第一个写入者成功""" self.revision += 1 if self.cur_leader is None: self.cur_leader = node_id self.term += 1 return True return False def renew(self, node_id): """模拟租约续期: 只有当前 Leader 能续约""" self.revision += 1 return node_id == self.cur_leader def expire(self): """模拟租约过期, Leader 身份失效""" self.revision += 1 self.cur_leader = None def main(): nodes = ["node-a", "node-b", "node-c", "node-d"] print("======== ZooKeeper 风格: 顺序节点小号当 Leader ========") zk = ZKStyleElection() orders = {} for n in nodes: orders[n] = zk.join(n) print("join %s seq=%s" % (n, orders[n])) print("当前 Leader ->", zk.get_leader()) zk.leave(zk.get_leader()) print("Leader 掉线后新 Leader ->", zk.get_leader()) print() print("======== Etcd 风格: 抢锁式选主 + 租约续期 ========") etcd = EtcdStyleElection() for n in nodes: ok = etcd.try_campaign(n) print("candidate %s granted=%s term=%s" % (n, ok, etcd.term)) if ok: break print("当前 Leader 续约 ->", etcd.renew(etcd.cur_leader)) print("非 Leader 续约 ->", etcd.renew("node-b")) etcd.expire() print("租约过期后 Leader ->", etcd.cur_leader) if __name__ == "__main__": main()
self.candidates[node_id] = self.sequence模拟 ZooKeeper 创建顺序节点,每来一个竞选者自动获得递增序号。return min(self.candidates, key=lambda n: self.candidates[n])选举规则:序号最小的节点当选 Leader,这就是“小号优先”。self.candidates.pop(node_id, None)模拟临时节点生命周期:节点掉线后,其在目录树上的“登记牌”自动消失。self.term += 1模拟 Raft 的任期(term),每产生一届 Leader 任期号就递增一次。if self.cur_leader is None:模拟 Etcd 的事务 CAS:只有锁为空时第一个写入者才能胜出。return node_id == self.cur_leader租约续期:只允许当前 Leader 在租约过期前续约,防止其他节点越权。etcd.expire()租约过期后 Leader 失效,所有节点重新回到竞选状态,对应“持有者崩溃自动让位”。
常见误区
误区听说 ZooKeeper 是 CP 系统,那它是不是一遇到网络分区就彻底罢工、一个请求都处理不了?
正解ZooKeeper 在分区时依然可以服务,只是“只服务多数派一侧”。如果集群 5 台中有 3 台处于同一分区,这 3 台仍能正常选主、读写;少数派一侧的节点会拒绝写请求,但读请求依然可以返回旧数据。Etcd 同样如此。两者都是“多数派可用”,而非“全部不可用”。
为何常见:很多人把 CAP 理论简化为“CP=不可用”、“AP=可用”,忽略了“多数派”这个关键前提,导致误判系统的真实容错能力。
误区ZooKeeper 有目录层级,Etcd 只是一张扁平的 key-value 表,那 Etcd 的表达能力是不是更差?
正解扁平 KV 配合 key 前缀扫描完全可以覆盖绝大多数场景,而且更简单。Etcd 的 lease 可以模拟临时节点,Txn 可以模拟顺序节点逻辑;而树形结构在 ZooKeeper 中还会带来“删除父节点必须递归删除子节点”的额外心智负担。模型的“强弱”取决于你要表达什么,而不是形态是否复杂。
为何常见:人们容易把“看起来更复杂”等同于“更强大”,忽视了扁平结构在工程上的简洁性和可维护性。
误区两者都叫 Watch,那我用 ZooKeeper 的注册一次、触发一次的习惯去写 Etcd 的监听逻辑应该没问题吧?
正解两者机制完全不同。ZooKeeper 的 Watch 是一次性触发,事件发生后必须重新注册,否则后续变更不再通知;Etcd v3 的 Watch 是长连接流式推送,注册一个前缀就能持续收到所有变更,直到客户端主动取消。把 ZK 习惯带到 Etcd,会漏掉大量更新事件。
为何常见:名字相同让人放松警惕,再加上很多教程只讲“能监听变更”,不讲触发后的生命周期差异,埋下了隐蔽的 bug。
误区Etcd 性能好、API 友好、又是云原生标配,那是不是所有 ZooKeeper 场景都能无脑迁移到 Etcd?
正解替代成本的关键在“生态绑定”而非“能力差距”。Kafka、HBase、Dubbo 等大量框架在代码层深度依赖 ZooKeeper 的临时节点、顺序节点和 watch 语义,迁移等于重写这些框架的协调模块。如果现有系统已经稳定运行,贸然迁移带来的风险远超收益。
为何常见:技术选型时人们容易把“新技术更好”当成充分条件,忽略了存量系统的兼容性成本和迁移风险。
误区只要用 ZooKeeper 顺序节点或 Etcd 事务锁实现了分布式锁,就能保证所有并发安全,万事大吉。
正解分布式锁只是“互斥”手段,真正的安全还要考虑持锁者被 GC 暂停导致锁提前过期、网络分区导致旧主“复活”等问题。业界标准做法是引入 fencing token(令牌)机制:每次加锁拿到一个递增令牌,写操作必须携带令牌,存储端拒绝过期令牌,才能彻底避免“两个主同时写”。协调服务能给你好锁,但不能替你挡住所有分布式陷阱。
为何常见:单机锁的心智模型让人误以为“抢到锁 = 安全”,忽略了分布式环境下网络延迟、GC 停顿、时钟漂移等额外威胁。
面试题
Q1. ZooKeeper 采用的数据模型是下面哪一种?
- A.树形分层目录结构← 正确答案
- B.扁平键值对存储结构
- C.文档型集合结构
- D.图结构递归查询结构
答案:A。ZooKeeper 以 znode 组成树形目录,类似文件系统,路径从根 / 开始逐层展开。扁平键值对是 Etcd 的模型,文档集合是 MongoDB 的风格,图结构则对应图数据库,均不符合 ZooKeeper 的特点。
Q2. Etcd 使用的一致性协议是?
- A.Raft 共识算法← 正确答案
- B.Paxos 投票协议
- C.ZAB 广播协议
- D.PBFT 拜占庭容错
答案:A。Etcd 是 Raft 协议最著名的工业级实现,靠领导者选举和日志复制达成共识。ZAB 是 ZooKeeper 自研协议,Paxos 是理论模型,PBFT 面向拜占庭故障,均不是 Etcd 的选择。
Q3. ZooKeeper 临时节点的生命周期与什么绑定?
- A.客户端会话存活时间← 正确答案
- B.服务端进程启动时间
- C.网络请求超时时间
- D.磁盘剩余空间大小
答案:A。临时节点随会话创建,会话断开节点自动删除。这正是“人走牌消”的服务发现机制。Etcd 用租约 Lease 实现类似能力,而服务端进程、网络超时、磁盘空间都与临时节点生命周期无关。
Q4. 一个 5 节点的 ZooKeeper 集群,最多可以容忍几个节点宕机?
- A.1 个节点宕机
- B.2 个节点宕机← 正确答案
- C.3 个节点宕机
- D.4 个节点宕机
答案:B。法定人数 q = ⌊5/2⌋ + 1 = 3,集群只要有 3 台存活就能选主和提交。宕机 2 台还剩 3 台,仍然满足多数派;宕机 3 台只剩 2 台,无法选出 Leader,系统拒绝服务。所以最多容忍 2 台宕机。
Q5. Etcd v3 提供的 Txn 事务接口,最突出的能力是什么?
- A.多个条件判断与更新原子执行← 正确答案
- B.批量写入大幅提升吞吐
- C.完全替代分布式锁全部功能
- D.自动完成数据分库分表
答案:A。Txn 允许在 if-then-else 结构中对多个键做版本比较并原子执行更新,类似数据库中的 CAS。批量写入(B)是性能优化而非事务本质;分布式锁只是 Txn 的应用之一(C 说法绝对);分库分表与 Txn 无关。
Q6. 关于 ZooKeeper 的 Watch 通知机制,下列描述正确的是?
- A.事件触发一次后需要重新注册← 正确答案
- B.通知会持续流式推送给客户端
- C.一次注册之后永久自动生效
- D.只有写事件能触发通知机制
答案:A。ZooKeeper 的 Watch 是一次性触发器:节点数据变化时回调一次,之后失效,必须重新注册才能继续监听。Etcd 的 Watch 才是持续流式推送。永久生效、只限写事件的说法均不准确。
Q7. ZooKeeper 集群中,读请求主要由以下哪个角色直接响应?
- A.任意 Follower 节点← 正确答案
- B.必须由 Leader 节点
- C.只能由客户端本地缓存
- D.只能由 Observer 节点
答案:A。ZooKeeper 的读请求可以被任意节点(Follower 或 Observer)直接处理,以此分散读压力;写请求才必须交给 Leader 广播到多数派。Observer 只是只读节点中的一种,并非唯一读通道,本地缓存更不是官方读路径。
Q8. 相比 ZooKeeper,Etcd 的以下哪些特征属于其优势?
- A.扁平 KV 接口更为简洁← 正确答案
- B.Watch 支持流式持续推送← 正确答案
- C.树形目录结构更直观
- D.云原生生态围绕它构建← 正确答案
答案:ABD。扁平 KV 让数据读写更简单,流式 Watch 省去反复注册,Kubernetes 等云原生体系将 Etcd 作为标准存储,都是 Etcd 的突出优势。树形目录是 ZooKeeper 的特征,并非 Etcd 优势,故 C 不选。
Q9. 设计一个可靠的分布式锁,需要正确处理哪些问题?
- A.持有者崩溃后自动释放锁← 正确答案
- B.持锁时间过长允许续约← 正确答案
- C.网络分区时避免出现双主← 正确答案
- D.加锁操作具备原子性和顺序性← 正确答案
答案:ABCD。临时节点或租约解决崩溃释放(A);租约续期防止持锁者业务未完成锁却被回收(B);多数派共识和任期编号防止分区后双主(C);ZooKeeper 顺序节点或 Etcd 事务保证加锁的原子与有序(D)。四个方向都是分布式锁的必备考虑。
Q10. 关于 ZAB 与 Raft 两种协议的差异,下列说法正确的有?
- A.ZAB 用 zxid 标识全局事务顺序← 正确答案
- B.Raft 用 term 与日志索引保证有序← 正确答案
- C.两者都要求 Leader 追加提交通知消息
- D.分区时两者都只服务多数派一侧← 正确答案
答案:ABD。ZAB 的 zxid 和 Raft 的 term+index 都是全局有序标识,说法 A、B 正确。分区时两类协议都只让多数派提供服务,D 正确。C 错误:ZAB 在提交阶段有明确的 commit 消息广播,而 Raft 的提交通过下一条 AppendEntries 中的 commitIndex 捎带通知,并非追加独立通知消息。