面试追问地图

主问题必讲关键点下一层追问
Znode持久/临时/顺序会话过期后节点何时删除
Session心跳、超时、重连断连与过期区别
Watch通知、一次性、重新读取丢事件和羊群效应
分布式锁临时顺序节点、监听前驱公平性、会话过期、fencing
ZABLeader、Proposal、ACK、CommitLeader 故障、zxid
集群节点数多数派和容错为什么 4 节点不优于 3 节点
注册发现注册、健康、订阅、本地缓存注册中心不可用如何继续调用
Nacos/ZK/Eureka/etcd/Consul一致性、生态、功能如何结合项目约束选型
配置中心版本、推送、灰度、回滚多实例短时不一致
Leader 选举选主流程、zxid 比较、epoch临时节点删除与业务安全退出

ZooKeeper 题必须区分“连接断开”和“Session 过期”,这是分布式锁与临时节点追问的关键。


一、ZooKeeper 基础

ZooKeeper 解决什么问题?

难度 🟡

ZooKeeper 是分布式协调服务,提供:

  • 命名和元数据管理。
  • 配置协调。
  • 服务发现。
  • 选主。
  • 分布式锁和屏障。

它适合存储少量协调数据,不适合作为大数据量业务数据库。

ZooKeeper 的数据模型是什么?

数据以树形 Znode 组织。常见节点类型:

  • 持久节点。
  • 临时节点。
  • 持久顺序节点。
  • 临时顺序节点。

临时节点与会话绑定,会话失效后自动删除;顺序节点带单调递增序号。

ZooKeeper 的一致性保证有哪些?

  • 写操作全序。
  • 同一客户端请求按发送顺序执行。
  • 已成功写入的数据不会丢失。
  • 客户端可能在短时间内从 Follower 读到非最新数据。

需要最新视图时可使用同步机制,但会增加延迟。不要简单描述成“所有读取都强一致”。


二、Session 与 Watch

ZooKeeper Session 如何工作?

难度 🔴

客户端与集群建立会话并周期性发送心跳。只要在 Session Timeout 内连接到任意可用节点,会话可继续存在。

连接断开不等于会话立即失效;只有服务端确认超时后,临时节点才会删除。

Watch 机制有什么特点?

  • Watch 是事件通知,不携带完整新数据。
  • 传统 Watch 通常一次触发,需要重新注册。
  • 事件可能合并,不能把 Watch 当作可靠消息队列。
  • 收到通知后应重新读取状态并重新设置监听。

什么是羊群效应?如何避免?

大量客户端监听同一节点,节点变化时同时被唤醒并争抢资源,形成瞬时压力。

分布式锁中让每个等待者只监听前一个顺序节点,可以将全量唤醒变为链式唤醒。


三、ZooKeeper 分布式锁与选主

ZooKeeper 如何实现公平分布式锁?

  1. 在锁目录下创建临时顺序节点。
  2. 查询所有子节点并排序。
  3. 最小节点获得锁。
  4. 其他节点只监听自己的前驱节点。
  5. 持有者删除节点或会话失效后,后继被唤醒。

它天然提供排队顺序,但需要处理会话过期、重复创建和业务幂等。

ZooKeeper 如何实现 Leader 选举?

参与者创建临时顺序节点,序号最小者成为 Leader,其他节点监听前驱。

选举结果只代表协调权,业务侧仍要防止旧 Leader 暂停恢复后继续写入,可结合 fencing token。

临时节点删除是否代表业务实例已经安全退出?

不一定。网络分区时实例可能仍在运行,但会话已过期并失去协调资格。实例恢复连接后必须重新注册,并停止使用旧身份执行关键写操作。

Curator 客户端与典型用法

频次 ★★★ · 难度 🟡

Curator 是 ZK 的 Java 高级客户端,封装了连接管理、重试策略和常见分布式协调模式。

连接与重试:

CuratorFramework client = CuratorFrameworkFactory.builder()
    .connectString("zk1:2181,zk2:2181,zk3:2181")
    .sessionTimeoutMs(15000)
    .retryPolicy(new ExponentialBackoffRetry(1000, 3)) // 指数退避重试
    .build();
client.start();

Curator Recipes(开箱即用的分布式原语):

Recipe实现原理对应原语
InterProcessMutex可重入分布式锁临时顺序节点 + Watch 前驱
InterProcessReadWriteLock读写锁读锁共享、写锁排他
LeaderSelectorLeader 选举 + 自动重新选举临时顺序节点,takeLeadership() 回调
ServiceDiscovery服务注册与发现基于命名空间管理节点
DistributedQueue分布式队列临时顺序节点 FIFO
PathCache / TreeCache路径/子树监听Watch + 缓存刷新

使用建议:

  • 始终指定重试策略(不可靠网络下指数退避比固定间隔更友好)
  • 锁超时搭配 @InterProcessMutex(leaseTime) 防止死锁
  • 使用 ConnectionStateListener 监听状态变化(SUSPENDED → 暂停本地操作,LOST → 清理本地状态)
  • Curator 5.x 支持 ZK 3.8+,推荐使用其 curator-recipes 而非自行实现 Watch 逻辑

四、ZAB 与集群

ZAB 的广播流程?

难度 🔴

  1. 写请求转发到 Leader。
  2. Leader 分配 zxid 并广播 Proposal。
  3. Follower 持久化后返回 ACK。
  4. 达到法定人数后 Leader 广播 Commit。
  5. 各节点应用事务。

通用概念:过半 ACK 才提交,依据同 Raft——见多数派。

ZooKeeper 为什么推荐奇数节点?

容错能力由多数派决定:

  • 3 节点容忍 1 个故障。
  • 4 节点仍只容忍 1 个故障。
  • 5 节点容忍 2 个故障。

偶数节点通常增加成本,却不提升相同级别下的故障容忍数。

Observer 节点有什么作用?

Observer 同步数据并服务读请求,但不参与投票,可扩展读能力而不增加共识投票规模。


五、注册中心

注册中心的核心职责是什么?

  • 服务实例注册和注销。
  • 健康检查。
  • 实例列表查询与订阅。
  • 元数据和路由信息管理。
  • 为客户端负载均衡提供节点列表。

注册中心通常不参与每次业务请求转发,客户端会缓存实例列表。

注册中心不可用时,服务还能调用吗?

如果调用方已有本地实例缓存,通常可以继续调用现有节点,但:

  • 无法及时发现新实例。
  • 无法及时剔除异常实例。
  • 服务重启后可能无法注册。

因此客户端需要缓存、健康探测和失败重试,但不能无限信任旧列表。

Nacos、ZooKeeper、Eureka、etcd、Consul 如何比较?

组件一致性模型存储后端特点常见场景
NacosAP/CP 可切换ephemeral=true 走 Distro 协议 AP,false 走 Raft CP)自研(临时实例存内存,持久实例用 Derby/MySQL)注册中心 + 配置中心,支持多种实例模型和健康检查Spring Cloud Alibaba
ZooKeeperCP(ZAB 协议,多数派写成功才确认)内存 + 事务日志 + 快照协调语义强,临时节点和 WatchDubbo、选主、分布式协调
EurekaAP(拒绝因网络分区牺牲可用性,自我保护模式)纯内存,多节点对等复制客户端缓存、去中心化对等复制存量 Spring Cloud Netflix 项目
etcdCP(Raft 协议)内存 + boltdb 持久化使用 gRPC 通信,支持 watch 和 lease,Kubernetes 默认存储K8s、配置管理、服务发现
ConsulCP/AP 混合(Raft 保证强一致,但 DNS 查询走弱一致)内存 + Raft 日志 + 快照内建健康检查、多数据中心、DNS/KV/Web 接口多数据中心服务发现、Consul-template 配 K8s 外的场景

选型思路:

K8s 环境     → etcd(K8s 自带,无需额外部署)
Spring Cloud → Nacos(功能最全,注册+配置一体化)
存量 Netflix → Eureka(已停更,考虑迁移)
纯粹协调    → ZooKeeper(成熟稳定,生态深厚)
多数据中心   → Consul(原生多 DC 支持,自带健康检查)

为什么注册中心通常选 AP 而非 CP:注册中心一次读到的”稍旧”实例列表,代价只是偶尔调用到一个刚下线的节点(有重试兜底);但 CP 模型在网络分区时拒绝写入/读取,会导致新实例注册不上、服务发现直接不可用——对注册发现这个场景,可用性优先级通常高于强一致。ZooKeeper 用于选主、分布式锁等场景才需要它的 CP 保证;直接拿来做注册中心(如早期 Dubbo)是”协调工具兼职”,Nacos/Eureka 是专门为此场景设计的。

选型还要考虑团队经验、生态、运维能力和既有基础设施;Eureka 2.0 已于 2018 年停止开发,Netflix 官方不再推进,新项目更推荐 Nacos 或已用 Consul/K8s 服务发现的方案。

推模式和拉模式如何结合?

常见方案是:

  • 服务端通知客户端实例变化。
  • 客户端定期拉取全量列表进行校准。
  • 本地缓存作为注册中心短时故障的兜底。

只推可能丢事件,只拉会增加延迟和服务端压力。


六、配置中心

配置中心和注册中心有什么区别?

  • 注册中心管理“服务在哪里、是否健康”。
  • 配置中心管理“服务如何运行”。

两者都涉及推送、版本、权限和高可用,但数据模型和变更风险不同。

配置变更如何保证安全?

  • 配置格式和取值校验。
  • 版本记录和审计。
  • 灰度范围。
  • 客户端失败时保留旧配置。
  • 关键配置支持快速回滚。
  • 避免同一变更同时触发所有实例重载。

参考资料