Kafka 为什么移除 ZooKeeper 依赖:技术深度分析
Apache Kafka 已经移除了长期以来对 ZooKeeper 的依赖,转而使用自管理的元数据系统 KRaft(Kafka Raft)。这一根本性的架构变更解决了限制 Kafka 可扩展性的关键技术问题,简化了操作,并显著提高了性能。KRaft 引入了基于 Raft 共识算法的事件溯源元数据系统,在 Kafka 3.3 中已经可用于生产环境,而在 Kafka 4.0 中完全移除了 ZooKeeper 支持。
ZooKeeper 的技术局限性日益凸显
ZooKeeper 曾为 Kafka 服务了十多年,但随着 Kafka 部署规模的扩大,其技术局限性变得越来越严重:
可扩展性遇到硬性上限
ZooKeeper 对 Kafka 施加了基本的可扩展性限制:
- O(N) 元数据操作:控制器初始化和主题管理需要从 ZooKeeper 加载完整的集群状态,操作时间随分区数量线性增长。这使集群实际上限制在约 200,000 个分区。
- 控制器故障转移延迟:当控制器发生故障时,整个集群基本上会暂停,直到新控制器从 ZooKeeper 加载完整状态。对于大型集群,这可能需要几分钟,导致不可接受的停机时间。
- 元数据加载性能:每个 broker 在启动时需要从 ZooKeeper 加载配置数据,即使是增量变更,控制器也需要重新加载所有主题信息。
一致性模型不匹配
ZooKeeper 的一致性模型与 Kafka 的需求不完全吻合:
- 双重状态管理:元数据同时存在于 ZooKeeper 和 Kafka 控制器的内存中,在这两个真实来源之间可能产生不一致。
- 监视机制限制:ZooKeeper 的监视通知只表明状态已更改,而不表明新状态是什么,需要额外读取并创建竞争条件。
- 通知延迟:当分区在 ZooKeeper 中更改其同步副本 (ISR) 时,控制器可能需要几秒钟才能了解这些更改,导致决策不够理想。
性能瓶颈
ZooKeeper 的设计创造了几个性能限制:
- I/O 约束:不是为高带宽系统设计的,在大型 Kafka 部署中,ZooKeeper 可能被大量客户端压垮。
- 基于推送的更新:控制器需要将更新推送到所有 broker,随着集群增长扩展性不佳。
- 连接开销:ZooKeeper 客户端连接维护心跳,消耗所有 broker 的资源。
KRaft 架构解决了这些根本问题
KRaft 代表了 Kafka 元数据管理的范式转变,通过全新方法解决了 ZooKeeper 的局限性:
事件溯源元数据模型
KRaft 最重要的创新是元数据的事件溯源方法:
- 元数据即日志:所有元数据变更都记录在特殊内部主题(
__cluster_metadata)中的事件日志中,创建了状态变更的完整历史记录。 - 确定性状态:通过从日志中重放事件,可以确定性地重构当前集群状态。
- 快照:定期快照提供高效的状态重构,无需重放整个日志。
这种模型消除了与 ZooKeeper 存在的双重状态问题,确保元数据的单一真实来源。
具有热备份的控制器仲裁
KRaft 引入了与普通 broker 分开的专用控制器角色:
- 控制器仲裁:一组通过 Raft 共识管理元数据的服务器(通常为 3 或 5 个)。
- 活跃控制器:仲裁的领导者,处理所有元数据变更。
- 热备控制器:保持所有元数据在内存中更新副本的跟随者,实现近乎即时的故障转移。
这种架构使控制器故障转移速度比基于 ZooKeeper 的控制器快 14 倍,因为新领导者已经在内存中拥有所有元数据。
基于拉取的元数据传播
KRaft 完全重新设计了元数据在系统中的流动方式:
- 增量更新:broker 可以接收增量元数据更新,而不是完整重新加载。
- 偏移量跟踪:broker 通过偏移量跟踪其在元数据日志中的位置,就像消费者跟踪其在常规主题中的位置一样。
- 高效传播:元数据变更通过日志流动,而不需要控制器直接向所有 broker 进行 RPC。
这种设计将大多数元数据操作从 O(N) 改进为 O(1),显著提高可扩展性。
性能提升显著
KRaft 在多个维度上提供了可量化的性能改进:
控制器故障转移和恢复
对于拥有 200 万个分区的 Kafka 集群(之前实际限制的 10 倍):
- 受控关闭时间:从 ZooKeeper 的 975 秒减少到 KRaft 的 123 秒(减少 87% )
- 非受控关闭后恢复:从 1,208 秒减少到 125 秒(减少 90% )
元数据操作
- 分区重新分配速度:移动 10,000 个分区的主题使用 ZooKeeper 需要 600 秒,而使用 KRaft 仅需 42 秒(快 93% )
- 主题创建:创建大量主题和分区时性能显著提升
吞吐量一致性
- 正常生产者/消费者工作负载的数据吞吐量在 ZooKeeper 和 KRaft 集群之间保持相同,这符合预期,因为元数据管理不影响数据路径。
操作复杂性大幅降低
除了性能外,KRaft 还带来了实质性的操作优势:
简化架构
- 单一系统:消除了在 Kafka 旁边部署和管理单独的 ZooKeeper 集群的需求。
- 统一安全模型:单一安全模型,而不是为每个系统使用不同的安全配置。
- 整合配置:单一配置集,而不是管理两个不同的系统。
简化部署
- 单一部署模型:所有组件的一致部署模式。
- 开发模式:支持轻量级组合模式(broker+控制器)用于开发环境。
- 迁移路径:提供将现有基于 ZooKeeper 的集群迁移到 KRaft 的工具。
更好的故障处理
- 故障隔离:系统之间没有级联故障。
- 改进的弹性:在服务中断期间 ZooKeeper 和 Kafka 之间没有协调问题。
- 更快恢复:事件驱动恢复使控制器能够快速赶上错过的事件。
促使变更的关键技术因素
促使替换 ZooKeeper 的其他几个技术因素:
架构不匹配
- API 限制:ZooKeeper 类似文件系统的 API 对于基于事件的元数据管理不是最佳选择。
- 大小限制:ZooKeeper 的 znode 大小限制约束了元数据存储选项。
- 不同范式:ZooKeeper 的协调中心设计与 Kafka 的事件流范式不同。
规模需求
现代 Kafka 部署需要支持:
- 数百万个分区:KRaft 使单个集群支持超过 200 万个分区成为可能。
- 更多 broker 数量:更高效地管理大型 Kafka 集群。
- 云原生模式:ZooKeeper 的设计早于许多云原生模式。
维护和功能开发
- 功能限制:ZooKeeper 限制了某些功能,如单节点模式和远程管理。
- 双重专业知识:管理员需要同时具备 Kafka 和 ZooKeeper 的专业知识。
- 开发开销:与 ZooKeeper 保持兼容拖慢了 Kafka 功能开发。
结论
移除 ZooKeeper 依赖代表了 Apache Kafka 的基础架构改进。通过实现带有 Raft 共识的事件溯源元数据系统,Kafka 消除了以前的可扩展性瓶颈,简化了操作,并显著提高了性能。
最显著的技术优势包括恢复时间快 90%,元数据操作快 93%,分区容量增加 10 倍,以及大幅简化操作。这些改进使基于 KRaft 的 Kafka 更适合处理现代数据流应用程序的需求,特别是在大规模环境中。
随着 Kafka 4.0 完全移除 ZooKeeper 支持,KRaft 代表了 Kafka 元数据管理架构的未来,使平台能够继续扩展,同时降低操作复杂性。