Kafka如何基于KRaft实现集群最终一致性协调
Kafka如何基于KRaft实现集群最终一致性协调
Apache Kafka从3.3.1版本开始引入了KRaft(Kafka Raft)作为Zookeeper的替代方案,用于实现集群的元数据管理和一致性协调。本文将详细介绍Kafka如何基于KRaft实现集群的最终一致性协调,包括Controller和Broker之间的通信协调机制。
架构概览
Zookeeper提供了配置服务、分布式同步、命名服务、Leader选举和集群管理等功能,在大数据时代初期,许多开源产品都依赖Zookeeper来构建,Apache Kafka也不例外。但是随着Kafka功能的演进和应用场景的增多,基于Zookeeper的协作模式暴露出以下问题:
- 集群一致性维护越来越复杂
- 受到Zookeeper性能限制,无法支撑更大规模的集群
- Zookeeper自身的运维复杂性和产品稳定性问题
因此,作为Zookeeper的替代方案,Kafka 3.3.1版本提供了KRaft元数据管理组件。下图展示了Zookeeper模式和KRaft模式的部署架构对比。
在Zookeeper(简称ZK)模式下:
- 运维部署:需要3个ZK节点和2个以上的Broker节点,其中一个Broker承担Controller的角色。这不仅增加了资源开销,还要求运维人员掌握两套不同系统的运维方式。
- 通信协调:ZK节点之间通过ZAB协议进行一致性协调;Broker通过ZK选出Controller,同时直接修改ZK中的数据;Controller监听和修改ZK中的数据,并调用Broker完成集群协调。这种多层协调机制使得一致性保障较为脆弱。
在KRaft模式下:
- 运维部署:只需要3个Controller节点和0个以上的Broker节点。Kafka节点可以同时承担Controller和Broker的角色,最小生产集群只需要3个节点,测试环境甚至可以单节点运行。
- 通信协调:Controller节点通过Raft协议达成一致,Controller的内存状态通过重放Raft Log构建,确保一致性;Broker订阅KRaft Log,保持与Controller一致的内存状态,并通过事件驱动方式执行分区重分配等操作,实现集群最终一致性协调。
最终一致性协调
最终一致性协调主要涉及两个方面:Controller内存数据与KRaft的一致性,以及Broker(分区/配置等)状态与期望的一致性。
Controller
Controller通常由3个节点组成Quorum,底层使用KRaft进行一致性协调,KRaft的Leader即为Controller Leader。只有Leader处理请求,Follower仅跟随重放KRaft数据。请求处理流程如下:
- 网络层接收到Broker请求后,将其放入事件队列,由后台单线程处理,简化并发编程复杂度。
- 单线程处理器运行请求对应的Manager逻辑,根据内存状态生成响应和变更Records。
- 将变更Records提交到KRaft,多数派确认后返回响应,并重放Records更新Manager内存状态。
- Follower也会重放KRaft Records,保持内存状态同步。
为了提高性能,实际处理模型将KRaft确认步骤异步化:
- Leader Manager生成Records后立即重放更新内存状态,并异步提交到KRaft。
- 响应仍需等待KRaft多数派确认。
- Follower内存状态仍从KRaft Log Records重放更新。
为解决可能出现的脏数据问题,Kafka设计了支持MVCC的Timeline数据结构(如TimelineHashMap、TimelineHashSet等),通过SnapshotRegistry实现数据回滚,确保内存状态一致性。
Broker
Broker通过订阅KRaft Records构建内存元数据,并根据这些Records执行特定变更。例如,分区管理场景中:
- Controller处理分区移动请求,生成PartitionChangeRecord提交到KRaft。
- 相关Broker重放变更记录,更新内存元数据并执行相应操作(如关闭或打开分区)。
这种方式使得Controller无需主动调用Broker RPC,而是通过KRaft下发期望状态,Broker负责达成状态,类似于K8s的声明式管理方式。
总结
KRaft替代Zookeeper,不仅仅是元数据存储的重构,更是集群协调机制的演进。整个通信协调机制本质上是事件驱动模型,即Metadata as an Event Log。Leader通过KRaft生成权威事件,Follower和Broker通过监听KRaft获得事件并顺序处理,实现集群状态与期望的最终一致。