Kafka 元数据管理

KIP-500

在Kafka2.8之前,Kafka一直使用Zookeeper[^1]来存储和管理Partition[^16]和Broker[^17]的元数据。以及选举一个Broker作为Kafka控制器

Kafka与Zookeeper[^23]

Kafka移除Zookeeper[^1]的动机

  1. 提高元数据管理的鲁棒性和可扩展性
  2. 支持更多分区
  3. 减少部署和配置难度

架构

image

当前,一个 Kafka 集群包含多个 broker 节点和一个外部的 ZooKeeper 仲裁节点。我们在这个图中描绘了 4 个 broker 节点和 3 个 ZooKeeper 节点,这是一个小型集群的典型规模。Kafka控制器(用橙色表示)在选举后从 ZooKeeper 仲裁节点加载其状态。控制器向其他 broker 节点推送更新,例如 LeaderAndIsr 和 UpdateMetadata 消息。

请注意,这个图有些误导。除了控制器,其他 broker 也会与 ZooKeeper 通信。因此,实际上应该从每个 broker 到 ZK 画线。然而,画这么多线会使图表难以阅读。另一个问题是,这个图省略了外部命令行工具和实用程序可以在没有控制器参与的情况下修改 ZooKeeper 中的状态。 如前所述,这些问题使得很难确定控制器内存中的状态是否真正反映了 ZooKeeper 中的持久状态。

在提出的架构中,三个控制器节点替代了三个 ZooKeeper 节点。控制器节点和 broker 节点在不同的 JVM 中运行。控制器节点为元数据分区选举出一个单一的领导者,显示为橙色。与控制器向 broker 推送更新不同,broker 从该领导者拉取元数据更新。这就是为什么箭头指向控制器而不是指向外的原因。

请注意,虽然控制器进程在逻辑上与 broker 进程是分开的,但它们不必在物理上分开。在某些情况下,将部分或全部控制器进程部署在与 broker 进程相同的节点上是有意义的。这类似于在较小集群中,ZooKeeper 进程可能与 Kafka brokers 部署在同一节点上的方式。与往常一样,所有部署选项都是可能的,包括在同一 JVM 中运行。

控制器的法定人数(Quorum)

控制器节点构成了一个Raft算法的仲裁集群,负责管理元数据日志。该日志包含关于集群元数据每次更改的信息。当前存储在 ZooKeeper 中的所有内容,例如主题、分区、ISR、配置等,都将存储在这个日志中。

使用 Raft算法[^10],控制器节点将在内部选举出一个领导者,而不依赖任何外部系统元数据日志的领导者称为活动控制器(active controller)。 活动控制器处理来自Broker[^17] 的所有 RPC 请求。活动控制器的从节点会复制写入活动控制器的数据,并在活动控制器发生故障时充当热备份。由于所有控制器都会跟踪最新状态,控制器的故障切换将不需要漫长的重新加载时间,因为我们不需要将所有状态转移到新控制器。

与Zookeeper[^1] 类似,Raft 要求大多数节点必须运行才能继续运行。因此,一个三节点的控制器集群可以承受一次故障。五节点的控制器集群可以承受两次故障,以此类推。 控制器会定期将元数据的快照写入磁盘。虽然在概念上这与压缩类似,但代码路径会有所不同,因为我们可以直接从内存中读取状态,而不是重新从磁盘读取日志。

broker元数据管理

与控制器向其他 broker 推送更新不同,这些 broker 将通过新的 MetadataFetch API 从活动控制器获取更新。模式由controller push 变为了 brokers pull

MetadataFetch 类似于 fetch 请求。与 fetch 请求一样,broker 将跟踪它最后获取的更新的偏移量,仅请求活动控制器的更新。 broker 会将获取的元数据持久化到磁盘,这将允许 broker 快速启动,即使存在成千上万甚至数百万个分区。

大多数情况下,broker 只需获取增量更新,而不需要获取完整状态。然而如果 broker 落后于活动控制器太多,或者 broker 根本没有缓存的元数据,控制器将发送完整的元数据快照,而不是一系列增量更新。

broker 会定期向活动控制器请求元数据更新。这个请求将同时充当心跳,向控制器表明 broker 仍然存活。

image

broker 状态机

在2.8版本之前 broker 在启动后会立即向 ZooKeeper 注册。这个注册完成了两个目的

  1. 让 broker 知道自己是否被选为控制器
  2. 让其他节点知道如何联系它。

之后则是broker将向控制器集群注册

image

Offline

当 broker 进程处于离线状态时,它要么完全未运行,要么正在执行启动所需的单节点任务,例如初始化 JVM 或执行日志恢复。

Fenced

当 broker 处于隔离状态时,它不会响应来自客户端的 RPC 请求。在启动并尝试获取最新元数据时,broker 将处于隔离状态。如果无法联系活动控制器,它将重新进入隔离状态。 隔离的 broker 应在发送给客户端的元数据中省略。

Online

当 broker 在线时,它准备好响应来自客户端的请求。

Stopping

当 broker 收到 SIGINT 信号时,它会进入停止状态。这表明系统管理员希望关闭 broker。在停止过程中,broker 仍然在运行,但我们正在尝试将分区领导者迁移到其他 broker。最终,活动控制器将通过在 MetadataFetchResponse 中返回特殊结果代码,要求 broker 最终进入离线状态。或者,如果无法在预定时间内迁移领导者,broker 将关闭。