面试追问地图
| 主问题 | 必讲关键点 | 下一层追问 |
|---|---|---|
| Znode | 持久/临时/顺序 | 会话过期后节点何时删除 |
| Session | 心跳、超时、重连 | 断连与过期区别 |
| Watch | 通知、一次性、重新读取 | 丢事件和羊群效应 |
| 分布式锁 | 临时顺序节点、监听前驱 | 公平性、会话过期、fencing |
| ZAB | Leader、Proposal、ACK、Commit | Leader 故障、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 如何实现公平分布式锁?
- 在锁目录下创建临时顺序节点。
- 查询所有子节点并排序。
- 最小节点获得锁。
- 其他节点只监听自己的前驱节点。
- 持有者删除节点或会话失效后,后继被唤醒。
它天然提供排队顺序,但需要处理会话过期、重复创建和业务幂等。
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 | 读写锁 | 读锁共享、写锁排他 |
| LeaderSelector | Leader 选举 + 自动重新选举 | 临时顺序节点,takeLeadership() 回调 |
| ServiceDiscovery | 服务注册与发现 | 基于命名空间管理节点 |
| DistributedQueue | 分布式队列 | 临时顺序节点 FIFO |
| PathCache / TreeCache | 路径/子树监听 | Watch + 缓存刷新 |
使用建议:
- 始终指定重试策略(不可靠网络下指数退避比固定间隔更友好)
- 锁超时搭配
@InterProcessMutex(leaseTime)防止死锁 - 使用
ConnectionStateListener监听状态变化(SUSPENDED → 暂停本地操作,LOST → 清理本地状态) - Curator 5.x 支持 ZK 3.8+,推荐使用其
curator-recipes而非自行实现 Watch 逻辑
四、ZAB 与集群
ZAB 的广播流程?
难度 🔴
- 写请求转发到 Leader。
- Leader 分配 zxid 并广播 Proposal。
- Follower 持久化后返回 ACK。
- 达到法定人数后 Leader 广播 Commit。
- 各节点应用事务。
通用概念:过半 ACK 才提交,依据同 Raft——见多数派。
ZooKeeper 为什么推荐奇数节点?
容错能力由多数派决定:
- 3 节点容忍 1 个故障。
- 4 节点仍只容忍 1 个故障。
- 5 节点容忍 2 个故障。
偶数节点通常增加成本,却不提升相同级别下的故障容忍数。
Observer 节点有什么作用?
Observer 同步数据并服务读请求,但不参与投票,可扩展读能力而不增加共识投票规模。
五、注册中心
注册中心的核心职责是什么?
- 服务实例注册和注销。
- 健康检查。
- 实例列表查询与订阅。
- 元数据和路由信息管理。
- 为客户端负载均衡提供节点列表。
注册中心通常不参与每次业务请求转发,客户端会缓存实例列表。
注册中心不可用时,服务还能调用吗?
如果调用方已有本地实例缓存,通常可以继续调用现有节点,但:
- 无法及时发现新实例。
- 无法及时剔除异常实例。
- 服务重启后可能无法注册。
因此客户端需要缓存、健康探测和失败重试,但不能无限信任旧列表。
Nacos、ZooKeeper、Eureka、etcd、Consul 如何比较?
| 组件 | 一致性模型 | 存储后端 | 特点 | 常见场景 |
|---|---|---|---|---|
| Nacos | AP/CP 可切换(ephemeral=true 走 Distro 协议 AP,false 走 Raft CP) | 自研(临时实例存内存,持久实例用 Derby/MySQL) | 注册中心 + 配置中心,支持多种实例模型和健康检查 | Spring Cloud Alibaba |
| ZooKeeper | CP(ZAB 协议,多数派写成功才确认) | 内存 + 事务日志 + 快照 | 协调语义强,临时节点和 Watch | Dubbo、选主、分布式协调 |
| Eureka | AP(拒绝因网络分区牺牲可用性,自我保护模式) | 纯内存,多节点对等复制 | 客户端缓存、去中心化对等复制 | 存量 Spring Cloud Netflix 项目 |
| etcd | CP(Raft 协议) | 内存 + boltdb 持久化 | 使用 gRPC 通信,支持 watch 和 lease,Kubernetes 默认存储 | K8s、配置管理、服务发现 |
| Consul | CP/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 服务发现的方案。
推模式和拉模式如何结合?
常见方案是:
- 服务端通知客户端实例变化。
- 客户端定期拉取全量列表进行校准。
- 本地缓存作为注册中心短时故障的兜底。
只推可能丢事件,只拉会增加延迟和服务端压力。
六、配置中心
配置中心和注册中心有什么区别?
- 注册中心管理“服务在哪里、是否健康”。
- 配置中心管理“服务如何运行”。
两者都涉及推送、版本、权限和高可用,但数据模型和变更风险不同。
配置变更如何保证安全?
- 配置格式和取值校验。
- 版本记录和审计。
- 灰度范围。
- 客户端失败时保留旧配置。
- 关键配置支持快速回滚。
- 避免同一变更同时触发所有实例重载。