| 问题 | 关键词/关键概念 | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Zookeeper是CP还是AP?| **CP系统**:强一致性、分区容错性<br>**牺牲可用性**:少数节点故障时不可用、选举期间不可用<br>**过半机制**:需要超过半数节点存活才能提供服务 | | Zookeeper选举机制?| **FastLeaderElection算法**<br>**投票规则**:epoch大优先→zxid大优先→myid大优先<br>**过半原则**:获得超过半数票当选Leader<br>**选举时机**:集群启动、Leader宕机 | | Zookeeper数据结构?| **树形结构**:类似文件系统<br>**ZNode节点**:持久节点、临时节点、顺序节点、容器节点(3.5+)<br>**数据限制**:每个节点1MB<br>**路径表示**:斜杠分隔如/app/config | | | | | | | | Zookeeper如何保证节点唯一?| **顺序节点**:自动追加10位递增序号<br>**原子性**:单线程处理、事务ID(zxid)<br>**版本控制**:数据版本(version)乐观锁<br>**路径唯一**:同级目录下节点名唯一 | | Zookeeper的watch机制?| **一次性触发**:触发后自动移除需重新注册<br>**事件类型**:NodeCreated/Deleted/DataChanged/ChildrenChanged<br>**异步通知**:服务端主动推送<br>**顺序保证**:先收到watch事件再看到变化 | | Zookeeper典型应用场景?| **配置中心**:统一配置管理<br>**服务注册发现**:Dubbo注册中心<br>**分布式锁**:临时顺序节点<br>**集群管理**:节点上下线感知<br>**分布式队列**:FIFO队列<br>**命名服务**:全局唯一ID | | Zookeeper的缺点?| **性能瓶颈**:写操作都在Leader、不适合大量写<br>**选举风暴**:大规模集群选举耗时<br>**网络分区**:脑裂问题<br>**GC影响**:长时间GC导致超时<br>**不适合大数据存储**:1MB限制 | | Zookeeper集群角色?| **Leader**:处理写请求、事务请求、发起投票<br>**Follower**:处理读请求、转发写请求、参与投票<br>**Observer**:处理读请求、不参与投票、扩展读性能 | | 什么是脑裂?如何解决?| **脑裂**:网络分区导致多个Leader<br>**ZK解决**:过半机制、epoch机制、少数分区不可用<br>**Fencing**:通过epoch/zxid隔离旧Leader | | 如何用Zookeeper实现分布式锁?| **步骤**:1.创建临时顺序节点 2.判断是否最小 3.不是则watch前一个 4.获得锁执行 5.删除节点释放<br>**公平锁**:按序号排队<br>**可重入**:记录持有者信息 | | 怎样使用Zookeeper实现服务发现?| **服务注册**:创建临时节点存储服务信息<br>**服务发现**:获取子节点列表<br>**健康检查**:session超时自动删除<br>**变更通知**:watch子节点变化 | # Zookeeper核心架构详解 ## **整体架构** ```Java 客户端层 ├── ZooKeeper Client API ├── Session管理 └── Watch管理 服务端集群 ├── Leader(领导者) │ ├── 处理写请求 │ ├── 事务提议 │ └── 数据同步 ├── Follower(跟随者) │ ├── 处理读请求 │ ├── 转发写请求 │ └── 参与投票 └── Observer(观察者) ├── 处理读请求 └── 同步数据(不投票) 存储层 ├── 内存数据库(DataTree) └── 事务日志(磁盘) ``` ## **ZNode节点类型** |节点类型|持久性|顺序性|用途| |---|---|---|---| |PERSISTENT|持久|否|普通数据存储| |PERSISTENT_SEQUENTIAL|持久|是|分布式队列| |EPHEMERAL|临时|否|服务注册、会话标识| |EPHEMERAL_SEQUENTIAL|临时|是|分布式锁| |CONTAINER(3.5+)|持久|否|容器节点| |TTL(3.5+)|持久|否|超时自动删除| ## **选举算法(FastLeaderElection)** ```java // 投票结构 class Vote { long id; // 服务器ID (myid) long zxid; // 事务ID long epoch; // 选举轮次 } // 选举流程 1. 初始投票给自己 2. 交换投票信息 3. 比较规则: if (vote.epoch > my.epoch) 更新 else if (vote.epoch == my.epoch) { if (vote.zxid > my.zxid) 更新 else if (vote.zxid == my.zxid && vote.id > my.id) 更新 } 4. 统计投票,超过半数则选举成功 ``` ## **ZAB协议(原子广播)** ### **两种模式** 1. **恢复模式**:选举Leader、数据同步 2. **广播模式**:处理客户端请求 ### **消息广播流程** ```Java Client → Leader → Proposal → Followers → ACK(过半) → Commit → Followers ``` ## **分布式锁实现** ```java // 获取锁 public boolean lock(String lockPath) { // 1. 创建临时顺序节点 String myNode = zk.create(lockPath + "/lock-", data, EPHEMERAL_SEQUENTIAL); // 2. 获取所有子节点 List<String> children = zk.getChildren(lockPath); Collections.sort(children); // 3. 判断是否是最小节点 if (myNode.equals(children.get(0))) { return true; // 获得锁 } // 4. 监听前一个节点 String prevNode = getPrevNode(myNode, children); zk.exists(prevNode, watcher); // 5. 等待通知 wait(); } // 释放锁 public void unlock() { zk.delete(myNode); } ``` ## **Watch机制原理** ```Java 注册Watch Client → Server: 注册监听器 ↓ WatchManager存储 触发Watch 数据变更 → WatchManager查找 → 异步通知Client → 移除Watch(一次性) 重要特性: - 一次性触发 - 异步通知 - 先收到事件,后看到新数据 - 不会丢失事件 ``` ## **性能优化** ### **集群规模建议** - **3节点**:测试环境,容忍1个节点故障 - **5节点**:生产环境推荐,容忍2个节点故障 - **7节点**:大型集群,容忍3个节点故障 - 避免偶数节点(无优势) ### **配置优化** ```properties # 会话超时(避免频繁重连) tickTime=2000 minSessionTimeout=4000 maxSessionTimeout=40000 # 同步限制 initLimit=10 # Leader选举超时 syncLimit=5 # 数据同步超时 # 快照和日志 dataDir=/data/zookeeper dataLogDir=/datalog/zookeeper # 分离日志 autopurge.snapRetainCount=3 autopurge.purgeInterval=1 ``` ## **常见问题及解决** |问题|原因|解决方案| |---|---|---| |**连接超时**|网络延迟、GC|调整sessionTimeout| |**选举风暴**|大量节点同时选举|使用Observer节点| |**性能下降**|写请求过多|读写分离、增加Observer| |**脑裂**|网络分区|过半机制自动处理| |**数据不一致**|异步同步|使用sync()强制同步| ## **与其他组件对比** |对比项|Zookeeper|Etcd|Consul| |---|---|---|---| |**一致性协议**|ZAB|Raft|Raft| |**语言**|Java|Go|Go| |**性能**|中|高|高| |**Watch**|一次性|持续|持续| |**使用复杂度**|高|中|低| |**主要用途**|配置、协调|K8s存储|服务发现|