如果你曾在公司中管理过Kafka部署,那么很可能处理过跨集群的Partition重组工作。由于Kafka的计算与存储紧密耦合,当集群成员发生变化(例如添加或移除Broker)或用户希望在Broker之间进行负载均衡时,必须重新分配Partitionreplicas,从而引发大规模数据迁移。
Kafka提供了用于处理重分配的脚本,但该过程依赖用户手动操作,且在规划层面缺乏稳健性。像AutoMQ2024这样的工具应运而生,能够基于集群状态实现副本的自动平衡,并制定更为复杂的重分配方案。然而,数据迁移的问题依然存在。本周我们将探讨AutoMQ如何应对Kafka的重平衡挑战。
AutoMQ是一款云原生解决方案,提供100%的Kafka协议兼容性,并将数据完全存储在ObjectStorage上。这种架构在保障低延迟与高吞吐性能的同时,实现了高性价比的Kafka替代方案。更重要的是,你再也无需在Broker之间传输数据。
KakfaPartitionsKafka的数据单位是Message,Kafka中的消息被组织在Topic中,你可以将Message类比为数据库系统中的“Row”,而主题就像“Table”,一个Topic会被划分为多个Partition。
每个Partition对应一份LogicalLog。从物理上看,这Log由若干个大小大致相同的SegmentFile组成,假设每个段为1GB。当一条Message写入Partition时,Broker会将其追加到最后一个SegmentFile中。
为了确保数据在系统中的持久保存和高可用性,Kafka会将每个Partition的数据复制到多个Broker上,复制的数量由配置中的ReplicaFactor决定。
这种副本机制可以在某个Broker发生故障时自动切换到其他副本节点,从而保证Message不会因节点宕机而丢失或不可用。每个Partition通常有一个负责读写的Leader,以及零个或多个用于备份的Follower。所有写入请求必须发送到Leader,而读取请求可以由Leader或对应的Follower处理。
为了避免高流量Topic的所有Partition被集中分配到少数节点,Kafka会采用轮询(Round-Robin)策略,将各个Partition的副本均匀分布到整个集群中,从而实现负载均衡并提升系统整体的稳定性和吞吐能力。
Kafka中的副本重新分配当Kafka集群中的副本被分布在不同Broker上时,如果某个现有Broker宕机,或者新增了一个Broker,会发生什么?这时候就需要对Kafka的副本进行重分配。
设想一个场景:我们有三个Broker,以及两个Partition,每个Partition拥有两个副本:
当某个Broker故障时,Kafka会自动将原本由该Broker作为Leader的Bartition的领导权重新分配给其他持有该副本的Broker。此外,为了维持原有的ReplicaFactor,Kafka可能还会在其他可用Broker上创建新的副本来补足。
当有新的Broker加入集群时,Kafka会重新分配副本,以保证不同Broker之间的负载更加均衡。
除了集群成员变更这种情况外,为了实现Broker间的负载均衡,Kafka也会需要进行Partition副本的重分配。合理分布数据可以防止“热点问题”,即某些Partition接收的流量明显高于其他Partition。同时,数据在Broker间的均匀分布有助于资源的最优利用。
Kafka的开源版本支持一个用于辅助Partition重新分配的工具,称为Kafka-reassign-partitions(Bin/)。该工具支持三种模式运行:
-generate:此模式用于创建Partition重分配计划;用户提供一组Topic和一组Broker后,工具会生成一个候选重分配计划,将这些Topic的Partition移动到新的Broker上。
-execute::此模式下,工具根据用户提供的重分配计划执行操作。该计划可以是用户手动创建的自定义计划,也可以通过--generate选项生成。
-verify:工具会验证上一次--execute操作中所有列出的Partition的重分配状态。
然而,该重分配过程通常需要用户手动完成,容易出错且效率低下。那么,有没有办法自动完成重分配操作呢?幸运的是,已有第三方工具专为此目的而开发。
Linkedln的CruiseControlCruiseControl是一款用于大规模运行ApacheKafka集群的工具。随着Kafka在企业中的广泛应用,越来越多公司面临管理大型Kafka集群的挑战。在LinkedIn,Kafka集群规模达到了约7000个以上的Broker,因此如何对Kafka的工作负载进行平衡成为一项非常复杂的任务。此外,大规模Kafka集群的监控与问题检测也同样至关重要。
CruiseControl提供了如下核心功能:
资源利用率追踪。
Kafka集群当前状态的可观测性。
Kafka集群的异常检测、警报,以及自愈功能。
添加和删除broker、集群重平衡等管理操作。
生成具有多种目标的重新分配计划。
CruiseControl依赖最近的副本负载信息对集群进行优化。它会定期收集Broker和Partition两个层级上的资源使用数据,以获取各Partition的流量模式。基于这些流量模式,CruiseControl可以评估每个Partition对Broker的负载影响,并据此构建集群的工作负载模型,从而模拟Kafka集群的运行状态。
其优化器(GoalOptimizer)会基于用户设定的一系列优化目标(Goals)探索多种优化方案,生成最合适的负载重分配建议。
这种方案与-Kafka-reassign-partitions有本质区别:Kafka原生工具仅基于用户输入的参数执行再平衡,而CruiseControl通过构建工作负载模型,能为再平衡计划提供更完善的目标策略集。
尽管CruiseControl能够有效降低再平衡操作的开销,但它仍无法避免跨Broker迁移数据所带来的网络传输开销。在数据在Broker之间迁移期间,Kafka集群需要一定时间来达到新的负载平衡状态。
这也意味着,使用CruiseControl或其他第三方工具进行负载均衡时,实际执行过程可能存在一定程度的不准确性:工具在执行优化决策时,是基于当时的集群快照进行操作的。而由于Kafka的数据需要进行副本同步,执行过程本身会比较缓慢。等到决策真正被执行时,集群的状态可能已经发生了较大变化,从而导致原有决策失效或效果下降。
这种问题在Kafka中始终存在,根本原因在于其设计理念:强调存储与计算的紧密耦合,这虽然带来了性能上的优势,但也限制了集群在负载动态调整方面的灵活性。
AutoMQ:无需数据迁移AutoMQ的设计让一切变得简单。AutoMQ在完全兼容ApacheKafka协议(100%Kafkaprotocol)的基础上,引入了共享存储架构,用以替代Kafka中broker的本地磁盘,其设计目标是实现完全无状态(Stateless)的系统架构。
传统KafkaBroker会将Message直接写入操作系统的页缓存(OSPageCache),而AutoMQBroker首先将Message写入堆外内存缓存(Off-heapMemoryCache),进行批量聚合后再写入对象存储(如S3)。为了保障在写入对象存储前出现故障时的数据持久性,AutoMQ引入了可插拔的预写日志(WAL,Write-AheadLog)机制。Broker在返回消息写入成功的确认(Ack)前,必须先将消息写入WAL中,然后再异步写入对象存储。如果Broker故障,AutoMQ会利用WAL中的数据进行恢复。
通过这种方式,AutoMQ实现了计算与存储的完全分离(Compute-storageSeparation)。这一架构设计意味着两个关键事实:
对象存储自身提供了数据的持久性与高可用性,因此无需在多个Broker之间复制数据。每个Partition只需保留一个副本,即Leader。
Broker完全无状态,它与Partition之间的关联仅通过Metadata维护,而非实际在本地磁盘上存储数据。
因此,AutoMQ的负载重分配过程变得极为简单:无需迁移任何数据,只需调整Broker与Partition之间的元数据映射关系。这使得决策能够快速、准确且高效地执行。
说到元数据,AutoMQ使用的是基于KafkaKraft模式(KRaftMode)的元数据管理架构。
最初Kafka使用ZooKeeper管理集群元数据,而Kraft模式则引入了内建的Raft协议控制器仲裁组(ControllerQuorum)。该仲裁组由一组Broker组成,负责维护和确保元数据的一致性。
在KRaft模式下,每个Broker都持有元数据的本地副本,而控制器仲裁组的Leader负责元数据的修改与更新,并将这些变更同步到所有Broker,从而降低了运维复杂度和潜在的故障点。
在AutoMQ中,控制器Leader保存了整个集群的元数据信息,包括Partition与Broker的映射关系。只有Leader才能修改这些元数据,其他Broker需通过与Leader通信来发起变更请求。元数据的变更会由控制器统一广播到所有Broker,确保整个集群的一致性。
AutoBalancer:AutoMQ自平衡功能TheGoals
Goal指的是用于指导Kafka集群优化与负载均衡的一组目标或约束条件。这些目标定义了具体要求,例如Broker间的负载分布、资源使用上限、Partition副本策略以及延迟控制标准等。
与CruiseControl提供预设目标并允许用户自定义不同,AutoMQ的自平衡功能AutoBalancer提供了一套简洁、稳定且经过充分验证的默认目标,无需用户额外配置。
每个目标都定义了一个阈值(Threshold)和一个可接受范围(AcceptableRange)。例如,一个用于平衡Broker负载的目标可能设定CPU使用率阈值为50%,接受范围为±20%,即30%到70%之间。只要指标落在该范围内,即认为达成Goal。AutoBalancer将Goal分为两类:
DetectionGoals:用于检测资源使用是否超出限制,例如CPU或网络I/O超载;
OptimizationGoals:主要用于执行集群流量的重新分配(ClusterTrafficRebalancing)。AutoMQ将这类目标进一步细分为Producer、Consumer和每秒查询数(QPS,QueryPerSecond)三种类型,不同的均衡目标对应不同的性能指标。例如,Producer、和Consumer的BalanceGoal旨在确保Producer和Consumer的流量在各个Broker之间分布合理,而QPS的BalanceGoal则用于平衡Broker之间的请求处理能力。这些优化目标相互配合,提升了集群的整体稳定性和资源使用效率。
为了确保在执行优化目标后系统效果的稳定性,AutoMQ会针对检测型目标和优化型目标分别精心设定阈值与范围。例如,收紧优化型目标的范围可以在目标执行后获得更精确的优化效果。
某些特定的Goal可能具有比其他目标更高的优先级。AutoMQ按照优先级将Goal分为两类:
HardGoal:必须在任何情况下都满足的目标,例如限制单个Broker上的Partition数量,或设置Broker流量的上限;
SoftGoal:在与HardGoal冲突时可以被忽略的目标,例如流量均衡等目标。
在Goal管理方面,AutoMQ使用数学模型对每个Goal进行建模。模型会根据特定的数学条件判断Broker是否满足对应的目标。在某些情况下,实现某个Goal的方式可能不止一种(例如,将某个Partition从BrokerA迁移到B或迁移到C都可能有助于平衡集群流量),AutoMQ会通过数学系统评估这些方案,并基于与Goal相关的参数对每个决策进行打分,最终执行得分最高的方案,从而实现最优决策。
组件
AutoBalancer的实现主要包括以下三个核心组件:
MetricsCollector(指标采集器):ApacheKafka提供了基于YammerMetrics和KafkaMetrics的指标采集系统,可通过MetricsRegistry和MetricsReporter接口进行监控。基于这些接口,AutoMQ实现了一个Reporter,用于周期性采集预定义的指标(如网络流量吞吐量等)。AutoMQ使用一个内部Topic在Broker与Controller之间传输指标数据;Reporter在采集完指标后,会将其封装为多条Message并发送至该内部Topic。
StateManager(状态管理器):在Controller端,AutoMQ维护一个ClusterModel,用于表示当前集群状态和各个partition的负载情况。集群中发生的变化(如Broker的增加/移除,Partition的重新分配或删除)会通过监听KRaft的元数据进行捕获,并同步更新ClusterModel。与此同时,Controller会持续消费内部Topic中的指标数据,对提取出的指标进行预处理,并更新ClusterModel,确保其始终准确反映集群当前状态。
DecisionScheduler(决策调度器):该组件的目标是帮助集群实现预期的调度效果,例如限制单个Broker上的Partition数量或控制单个Broker的流量。在AutoMQ中,仅有ActiveController负责执行决策与调度。决策开始前,AutoMQ会对当前的ClusterModel进行快照,并基于该快照状态进行后续调度过程。快照完成后,ClusterModel可继续更新。AutoMQ的决策调度过程采用类似CruiseControl的启发式调度算法。
典型流程
接下来我们深入了解AutoMQ自平衡(Self-Balancing)的典型流程(TypicalProcess):
每隔一个固定时间间隔(例如每60秒),自平衡调度器会启动一次,检查当前集群是否满足所有的Goal。如果全部满足,调度器将进入休眠状态;
如果未满足,调度器会获取违反Goal的Broker列表;
对于每个不符合要求的Broker,调度器会生成Partition重分配(Reassignment)计划,以尝试使该Broker满足对应的Goal;
调度器随后会判断该重分配计划是否对该Broker可行。如果可行,调度器就会在集群上执行该计划;如果不可行,则认为该Broker当前无法满足Goal,调度器将继续检查列表中的下一个Broker。
应用场景
下面我们来回顾AutoBalancer在不同场景下的行为:
TopicCreation:AutoBalancer支持在Topic创建时进行机架感知(RackAwareness)。它支持在多个Rack间随机分布数据的同时,考虑每个Rack的“Weight”。权重大(Weight较高)的Rack将比权重轻的Rack分配到更多的数据。在同一Rack内,数据也会依据Broker的权重分布,如果某个Broker的权重更高,它在该Rack内将获得更大的数据份额。
AddingBrokers:AutoBalancer支持对新增Broker进行逐步“预热”。系统不会一下子将全部流量发送到新Broker,而是会在一段时间内逐渐引导流量,以避免其负载过高。AutoBalancer也会尽可能减少扩容过程中的跨Rack流量,以避免网络拥塞,除非涉及新增Rack。
RemovingBrokers:AutoBalancer支持在Broker被移除后自动迁移其负责的Partition到其他Broker。它会优先尝试将Partition迁移到与原Broker属于同一Rack的其他Broker。
UnbalancedThroughput:系统会根据各个Broker处理请求的能力分配流量。每个物理Broker拥有一个“Weight”,表示其承载负载的能力。例如,更强大的Broker会被赋予更高的Weight。AutoMQ会综合考虑网络、IO或CPU核心数等因素来评估Broker的Weight。系统会持续监控每个节点的负载和处理能力,并根据情况调整调度策略,以防止某个Broker被过度使用。
SingleNodeFailures:AutoBalancer支持识别运行缓慢的Broker,这类Broker可能存在潜在问题。系统可以通过将任务迁移至健康节点来降低这些慢节点的负载,从而在不影响整体性能的前提下让其恢复。
AutoBalancer与CruiseControl对比
在结束本文之前,我们来回顾一下AutoBalancer与CruiseControl的一些区别:
AutoMQ原生支持AutoBalancer功能,无需进行复杂的运维操作和部署;相比之下,CruiseControl需要独立部署并与Kafka集群配套管理。
ApacheKafka在进行Partition迁移以实现流量均衡时,需要复制大量数据,执行成本较高。因此,CruiseControl的Goal设计较为严格,仅在流量波动较小的场景下效果较好。对于负载变化剧烈的场景,CruiseControl难以保持有效。而AutoMQ通过其计算与存储分离的架构,更能应对复杂的负载变化场景。
得益于设计优势,AutoMQ允许AutoBalancer更快速地执行Replica的重新分配。此外,由于AutoBalancer是AutoMQ的内建组件,它可以直接消费KRaft日志,从而能更快速地响应集群变更。
结语随后,我们探讨了CruiseControl等第三方工具如何以更便捷和更稳健的方式辅助用户完成这一流程。我们发现,AutoMQ能够彻底解决迁移过程中的数据移动难题,因为其数据存储在Broker之外,仅需调整Metadata即可完成迁移。
最后,我们深入介绍了AutoMQ的自平衡功能——AutoBalancer。我们看到,虽然CruiseControl能够帮助用户更高效地完成Kafka的Replica重分配过程,但其核心问题依然存在:数据仍需在Broker间通过网络传输。而AutoMQ的创新架构使数据完全存储于对象存储中,大大简化了Kafka的许多操作,尤其是在Partition重新分配时,仅需调整Metadata即可完成,从而使其内部的自平衡机制更加高效可靠。
至此,本文即将结束。我们下次再见。
参考资料
[1]WiththehelpofKaimingWan,DirectorofSolutionsArchitectLeadEvangelist@AutoMQ
[2]AutoMQofficialdocumentation
[3]AutoMQblog
[4]Confluent,BestPracticesforKafkaProduction
[5]KafkaCruiseControlGithubRepo





