互联网技术 / 互联网资讯 · 2024年1月4日

Kafka 为何逐步摆脱 Zookeeper

Kafka 未来版本逐步弱化并移除对 Zookeeper 的依赖,这对很多用户来说是一次重要演进。过去部署 Kafka 往往需要同时维护 Zookeeper 集群,而在新的架构方向下,Kafka 将朝着更独立、更易运维的方式发展。

一、Kafka 是什么

Apache Kafka 最初由 LinkedIn 开发,后续捐赠给 Apache 基金会。它被定义为分布式流式处理平台,因具备高吞吐、可持久化和可横向扩展等特性,被广泛应用于数据传输与实时处理场景。

从能力上看,Kafka 主要可以承担以下角色:

  • 消息队列:用于系统解耦、流量削峰、缓冲和异步通信。
  • 分布式存储:消息可持久化保存,并通过多副本机制实现故障转移。
  • 实时数据处理平台:可结合 Kafka Streams、Kafka Connect 等组件完成实时处理任务。

下面是 Kafka 的消息模型示意图:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

理解 Kafka 时,可以先掌握几个核心概念:

  • Producer:生产者,负责向 Kafka 写入消息。
  • Consumer:消费者,负责从 Kafka 拉取消息。
  • Consumer Group:消费者组,同一组中的多个消费者可并行消费同一 Topic 下不同分区的数据。
  • Broker:Kafka 集群中的服务器节点。
  • Topic:消息的逻辑分类。
  • Partition:Topic 的物理分片。每个分区中的消息都有有序的 offset,用于定位和消费进度管理。

在同一个消费者组内,一个分区在同一时刻只能被一个消费者消费,这也是 Kafka 实现消费并行与顺序保证的重要基础。

二、Kafka 与 Zookeeper 之间的关系

在传统架构中,Kafka 的运行高度依赖 Zookeeper。下面是经典架构示意:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

从这个架构可以看出,Zookeeper 并不是一个可有可无的组件,而是承担了 Kafka 大量元数据管理与协调工作。

再看一张更具体的关系图:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

1. 注册中心作用

Zookeeper 在 Kafka 体系中首先扮演的是注册中心角色,主要负责 Broker、Topic 和消费者相关信息的登记与维护。

1)Broker 注册

Kafka 集群通常由多个 Broker 组成,因此需要一个统一的地方维护节点信息。Zookeeper 会通过 /brokers/ids 路径保存 Broker 列表。

当 Broker 启动时,会向 Zookeeper 发起注册请求,在 /brokers/ids/[0...N] 下创建对应节点,并写入自身的 IP 地址和端口等信息。

这些节点通常是临时节点,一旦 Broker 宕机或与 Zookeeper 失去会话,该节点会自动删除,这样集群中的其他角色就能感知节点变化。

2)Topic 注册

每个 Topic 也会在 Zookeeper 中保存对应信息,路径通常类似于 /brokers/topics/[topic_name]

由于一个 Topic 会被拆分为多个 Partition,而每个 Partition 又可能分布在不同 Broker 上,因此这些分区与 Broker 之间的映射关系也需要由 Zookeeper 保存。

同时,Partition 一般会配置多副本,其中会有一个 Leader 副本对外提供读写服务。若 Leader 所在 Broker 故障,就需要重新选举新的 Leader,这一过程在旧架构中也离不开 Zookeeper 协调。

例如,查看某个 Topic 分区状态时,可以看到类似如下信息:

[Root@Master] get /brokers/topics/xxx/partitions/1/state {"controller_epoch":15,"leader":11,"version":1,"leader_epoch":2,"isr":[11,12,13]}

当 Broker 退出时,Zookeeper 还需要同步更新对应 Topic 的分区列表和状态信息。

3)Consumer 注册

消费者组也会把相关信息注册到 Zookeeper。通常会在 /consumers/{group_id} 路径下维护一组数据,用于保存消费者、分区归属以及消费位点等信息。

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

借助这些节点,系统可以记录分区与消费者之间的对应关系,以及每个分区的 offset 进度。

2. 负载均衡作用

除了注册中心功能,Zookeeper 还在一定程度上参与了 Kafka 的负载均衡。

Broker 注册到 Zookeeper 后,生产者可以感知 Broker 列表变化,从而把流量分发到不同节点。消费者组中的消费者也可以根据 Topic 和分区信息,分别拉取各自负责的数据分区,实现并行消费。

实际上,Kafka 在 Zookeeper 中保存的元数据远不止这些,内容相当庞杂:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

随着 Broker、Topic 和 Partition 数量不断增长,Zookeeper 中维护的数据规模也会持续上升,这正是后续问题的源头之一。

三、Controller 在 Kafka 中的作用

既然 Kafka 对 Zookeeper 依赖很深,那么它们之间具体是如何协作的?关键角色就是 Controller。

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

在传统架构里,Kafka 集群会从多个 Broker 中选出一个 Controller。这个节点负责与 Zookeeper 交互,并管理整个集群中分区、副本以及状态变化等核心事务。其他 Broker 则监听 Controller 的相关变更。

Controller 的选举本身也依赖 Zookeeper。选举成功后,Zookeeper 会创建一个 /controller 的临时节点,用于标识当前生效的 Controller。

Controller 的职责主要包括:

  • 监听分区变化。
  • 在分区 Leader 故障时发起新的 Leader 选举。
  • 在 ISR 集合变化时通知 Broker 更新元数据。
  • 在 Topic 新增分区时重新分配分区信息。
  • 监听 Topic 变化和 Broker 变化。
  • 维护集群整体元数据。

下面这张图展示了 Controller、Zookeeper 和 Broker 之间更细致的交互关系:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

Controller 选举完成后,会先从 Zookeeper 拉取完整元数据,用来初始化本地的 ControllerContext 缓存。之后,一旦集群中出现变化,例如增加 Topic、增加分区或 Broker 上下线,Controller 不仅要更新自身缓存,还要把这些变化同步给其他 Broker。

这些事件通常会被顺序放入队列,再由专门线程依次处理。由于其中大部分处理逻辑都要和 Zookeeper 交互,因此 Controller 的工作效率很大程度上会受到 Zookeeper 状态和元数据规模的影响。

四、Zookeeper 给 Kafka 带来的问题

Kafka 本身已经是一个分布式系统,却还要依赖另一个分布式系统来完成关键协调,这无疑提升了整体复杂度。随着集群规模增大,这些问题会越来越明显。

1. 运维复杂度高

部署 Kafka 时,往往需要同时部署和维护 Zookeeper,这意味着系统并不是一套,而是两套。运维人员不仅要熟悉 Kafka,还必须具备 Zookeeper 的维护能力,这会明显提高使用门槛。

2. Controller 故障恢复成本高

传统模式下,Kafka 依赖单一 Controller 与 Zookeeper 交互。一旦这个 Controller 所在 Broker 出现故障,就需要重新从其他 Broker 中选出新的 Controller。

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

新的 Controller 上任后,需要重新从 Zookeeper 拉取完整元数据进行初始化,并通知所有 Broker 更新当前活动 Controller 的标识。与此同时,旧 Controller 还要停止监听、事件处理线程和定时任务。

如果集群中的分区数量非常庞大,这个切换过程会比较耗时,而且在这段时间内,集群可用性会受到明显影响。

3. 分区规模受限

随着分区数量增加,Zookeeper 中维护的元数据会越来越多,集群压力也会增大。当规模上升到一定程度后,监听延迟、事件处理速度和状态同步效率都会下降,进而影响 Kafka 的整体性能。

这使得单个 Kafka 集群可承载的分区总量存在瓶颈,而恰恰有不少业务场景对分区规模要求很高。

五、Kafka 的升级方向

Kafka 在新架构中对这套机制进行了重构。升级前后的架构差异如下:

Kafka能有什么坏心思,不过是被Zookeeper害惨了……

新的思路是使用 Quorum Controller 替代旧的单一 Controller,并通过 KRaft 协议来管理元数据一致性。Quorum 中的每个 Controller 节点都会保存完整元数据,因此即使某个 Controller 节点故障,新的 Controller 接管也会更快,恢复成本更低。

这种模式本质上是把原来依赖 Zookeeper 保存和协调的元数据管理能力,直接内聚进 Kafka 自身。

官方给出的方向是,在这种新架构下,Kafka 可以更轻松地支持百万级分区规模。

KRaft 可以理解为 Kafka 基于 Raft 协议实现的元数据管理模式,全称为 Kafka Raft Metadata Mode。它通过 Raft 一致性协议完成元数据复制与选主,减少了对外部协调系统的依赖。

由于 Kafka 的用户规模很大,升级过程中保持业务连续性非常重要,因此兼容与平滑迁移一直是演进中的关键考虑。

去除 Zookeeper 的相关改造已经逐步落地,并在后续版本中不断完善。新旧控制模式在一段时间内并存,也为用户提供了灰度测试和分阶段迁移的空间。

六、总结

在大规模集群和云原生场景下,Kafka 长期依赖 Zookeeper 所带来的运维成本、故障恢复压力和扩展瓶颈,已经越来越明显。将元数据管理能力收回 Kafka 自身,是架构简化和性能提升的自然方向。

从这个角度看,Kafka 摆脱 Zookeeper 并不是一次简单的组件替换,而是一次面向大规模、高可用和低复杂度的系统重构。