Kafka 为什么移除 ZooKeeper 依赖:技术深度分析

234 阅读7分钟

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 元数据管理架构的未来,使平台能够继续扩展,同时降低操作复杂性。