孟月昊 林荣霞 冯文 刘全兵
摘 要: 在实际应用Kafka系统过程中,常因分区策略选择不当而导致系统负载不均衡,节点经常下线,影响业务工作。文章从消费速率变化和CPU使用率两个方面切入,研究分析了RangeAssignor、RoundRobinAssignor、StickyAssignor三种分区策略对Kafka在系统开销和负载均衡方面的影响,得出了这三种分区策略对系统影响的特点,对在实际生产应用Kafka过程中分区策略的选择和使用,起到了一定的参考作用。
关键词: 分布式消息系统; Kafka; 分区策略; 系统开销
中图分类号:TP301.6 文献标识码:A 文章编号:1006-8228(2020)11-11-05
Abstract: In the actual application of the Kafka, the system load is often unbalanced due to improper partitioning strategy selection, which results the nodes go offline and affects business work. In order to solve the problem, this paper focus on the changes of consumption rate and CPU utilization rate, studied and analyzed the influence of RangeAssignor, RoundRobinAssignor and StickyAssignor on system overhead and system stability when Kafka applied, and summarized the characteristics of the three partition strategies. This paper offers a reference for the selection and usage of partition strategy in the actual application of Kafka.
Key words: distributed messaging system; Kafka; partitioning strategy; system overhead
0 引言
随着大数据技术的广泛使用,分布式消息系统的应用也越来越广泛。目前分布式消息系统大多采用消息中间件的分布式架构,而处理消息一般采用发布订阅模式[1-3]。目前比较典型的分布式消息系统主要包括Microsoft MSMQ、RabbitMQ[4]以及Kafka等。其中Kafka相比其他分布式消息系统,在消息派发方面有着独特的优势,它以高吞吐量、水平扩展、可靠性高等特性而被广泛使用,能够收集和提交海量日志数据,并处理实时和离线数据[5-7]。
Kafka集群主要包含生产者(producer),消费者(consumer),消息中间件处理节点(broker)以及一个zookeeper集群[8]。Kafka按照建立的主题(topic)对收集的消息进行分类放置,并在topic内部划分分区(partition),并将partition中的消息分配给consumer消费。此外,Kafka通过zookeeper集群,对producer、consumer以及broker进行注册,在partition中选举leader消息副本对外提供主要服务,且在consumer数量发生改变时进行负载均衡;producer使用push模式将消息发布到broker;consumer使用pull模式从broker中订阅消费消息[9-10],如图1所示。
其中Kafka负载均衡[11]是一个重要的核心功能,它根据消费者数量的变化来对partition进行重分配,目前Kafka主要有三种分区策略[12]:RangeAssignor、RoundRobinAssignor、StickyAssignor。在实际应用过程中,常因分区策略选择不当,导致Kafka系统负载不均衡,性能不够稳定,节点下线,影响了业务工作与经济生产。另一方面,有关这三种分区策略的资料文献大部分是对算法思想和流程的分析。对于Kafka实际应用中如何选择分区策略以及不同分区策略对系统性能的影响研究分析的却很少,无法为分区策略的选择提供参考。
针对三种分区策略选择和使用的问题,本文从分区策略对系统性能影响的方面切入,通过实验数据研究分析对比了consumer消费速率变化以及CPU使用率两个方面,并得出三种分区策略对系统性能影响的特点,为Kafka实际应用中如何选择分区策略提供了一定的参考。
1 分区策略算法概述
1.1 RangeAssignor分区策略
RangeAssignor策略的原理是按照consumer总数和partition总数进行整除运算来获得一个跨度n,然后将partition按照跨度进行平均分配,以保证partition尽可能均匀地分配给所有的consumer。对于每一个topic,RangeAssignor策略会将消费组内所有订阅这个topic的consumer按照名称的字典序排序,然后为每个consumer进行分区分配,如果不能平均分配,那么字典序靠前的consumer会被多分配一个分区。分配策略如下:
跨度[n=分区数消费者数量],余数m=分区数%消费者数量
前m个消费者每个分配n+1个分区,后(消费者数量-m)个消费者每个分配n个分区。假设有一个topic,4个partition,在同一个消费组内consumer数量不同的情况下,partition分配如圖2所示。
⑴ partition/consumer能被整除。n=4/2=2,m=0能够整除,所以每个consumer能够均分到2个partition,如图2(a)所示。四个分区二个consumer的场景,分配结果如下:C0:[P0,P1],C1:[P2,P3]。
⑵ partition/consumer不能被整除,且新增consumer之后,consumer总数量小于partition数量n=4/3=1···1,m=1,所以第一个consumer分配1+1=2个partition,后面4-1=3个consumer每个分配一个分区,如图2(b)所示。
四个分区三个消费者的场景,分配结果如下:C0: [P0,P1],C1:[P2],C2:[P3]无法完全平衡分配的场景,排序更靠前的消费者分配到更多的分区。
当新增越多的topic,则分配不平衡越明显。例如再新增一个partition为4的Topic,分配情况如图3所示。
分配结果:订阅2个Topic,每个Topic四个分区,共3个Consumer。C0:[T0P0,T0P1,T1P0,T1P1],C1:[T0P2,T1P2],C2:[T0P3,T1P3],这种不平衡的情况会越来越严重。
⑶ 当consumer数量大于partition数量时,排序靠前的consumer能先分到partition,排序靠后的consumer可能分不到partition,且partition是均分给consumer,如图2(c)所示。
四个分区五个消费者的场景,分配结果为:C0: [P0],C1:[P1],C2:[P2],C3:[P3],C4:[P4]消费者数量超过分区数量时,排序更靠前的消费者先分配到分区,排序靠后的消费者可能分配不到分区。
1.2 RoundRobinAssignor分区策略
RoundRobinAssignor策略的原理是将所有consumer以及所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将partition依次分配给每个consumer。
⑴ 如果同一个消费组内所有consumer订阅的topic都是相同的,那么分配结果是尽量相同的,如图4所示,分配结果:C0:[T0P0,T0P3],C1:[T0P1],C2:[T0P2]。无法完全平衡分配,排序靠前的consumer分配到更多的分区。
⑵ 如果同一个消费组内所有的consumer订阅的topic是不相同的,那么分配的分区不能保证尽可能的均匀。假设有三个消费者分别为C0、C1、C2,有3个Topic T0、T1、T2,分别拥有1、2、3个分区,并且C0订阅T0,C1订阅T0和T1,C2订阅T0、T1、T0,没有订阅对应topic的consumer不参与分配,则分配结果如下:C0:[T0P0],C1:[T1P0],C2:[T1P1,T2P0,T2P1,T2P2]。如图5所示。
从分配结果可以看出,完全可以把T1P1交给C1进行消费,使得分区分配更加均衡。
1.3 StickyAssignor分区策略
针对RangeAssignor和RoundRobinAssignor分区分配不均衡的问题,从Kafka0.11x版本开始引入了StickyAssignor算法以优化分区分配。算法的目标主要有两点:一是分区的分配尽量均衡;二是每次重分配的结果尽量与上一次分配结果保持一致。当两个目标发生冲突时,优先保证第一个目标。第一个目标是每种分配算法都尽量尝试去完成的,而第二个目标才真正体现出StickyAssignor特性,即每一次分配变更相对上一次分配做最少的变动。
⑴ 同一消费组内所有consumer订阅的topic相同
假设有4个topic:T0,T1,T2,T3,每个topic有2个partition。共有3个consumer:C0,C1,C2,所有consumer都訂阅了这四个分区。StickyAssignor具体分配过程如图6所示。
StickyAssignor初始分配的结果与RoundRobin-Assignor相同,结果为C0:[T0P0,T1P1,T3P0],C1:[T0P1,T2P0,T3P1],C2:[T1P0,T2P1]。当C1下线之后,按照尽量少移动分区的原则,只对C1的分区T0P1,T2P0,T3P1进行分配,结果如下:C0:[T0P0,T1P1,T3P0,T2P0],C2:[T1P0,T2P1,T0P1,T3P1],最终达到C0,C1分区平衡。
⑵ 同一消费组内所有consumer订阅的topic不同
仍以RoundRobinAssignor所有consumer订阅topic的例子为例并与其进行对比,如图7所示。
RoundRobinAssignor分配结果为:C1:[T0P0,T1P1],C2:[T1P0,T2P0,T2P1,T3P1]。StickyAssignor分配结果为:C1:[T0P0,T1P0,T1P1],C2:[T2P0,T2P1,T2P2]。从分配过程可以看出,StickyAssignor的分配策略比RangeAssignor、RoundRobinAssignor更加均衡和优化。
2 分区策略性能分析与对比
实验环境所用的三台主机配置为CPU Intel Corei7-6700 3.4GHz,内存8GB,操作系统Redhat7.2,Kafka相关组件版本为Apache-zookeeper-3.5.5,Kafka_2.11-2.2.0。
三台主机分别为broker0,broker1,broker2。创建三个topic,记为t0,t1,t2,t0创建4个partition,t1创建4个partition,t2创建5个partition。在三台主机上分别创建1个consumer,分别记为C0,C1,C2,且三个consumer均属于同一个消费组。其中C0订阅t0中的消息,C1订阅t0,t1中的消息,C2订阅t0,t1,t2中的消息。创建producer,并利用Kafka生产者相关命令随机生成10000000条数据,每条长度为1000字节,存入本地磁盘。
2.1 对consumer消费速率变化影响的对比
修改Kafka配置文件中consumer.properties文件,将分区策略参数partition.assignment.strategy的值依次设为RangeAssignor、RoundRobinAssignor、StickyAssignor,其他所有参数和其他配置文件均保持相同。
启动C0,C1,C2对消息进行消费。待消费速率稳定后,在某一时刻将C0下线,每隔5s记录一次C1,C2的消费速率,并计算出消费速率增量,记为?V1和?V2,则每种分区策略的消费速率增量记为?V=?V1+?V2。同理,在第25s时候将C0上线,记录?V值。如表1所示,记录了C0下线和上线两种情况下,三种分区策略?V的值。图8为根据表1记录的数据生成的消费速率增量与时间图。
如图8所示,根据Kafka的consumer获得的 partition越多,消费速率越大,反之越小的特性。当C0下线时,所有topic和partition会进行重分配。C0订阅的partition会分配给C1和C2,所以C1和C2的消费速率都会增大,但是StickyAssignor策略比另外两种策略速率增量较小,增量趋势也相对平缓。而RangeAssignor和RoundRobinAssignor两种策略速率增量较大,增量趋势相对更加陡峭。当 C0上线时,同样会进行topic和partition的重分配,C1和C2的分区会有所减少,所以C1和C2的消费速率会下降,但速率增量的变化情况和C0下线时一样,StickyAssignor策略比另外两种策略要平缓。因此,从总体可以看出,StickyAssignor分区策略要比RangeAssignor和RoundRobinAssignor两种策略在应对partition变动以及重分配的情况时,对con-sumer消费消息影响较小,不会有剧烈的变化。这点对于在实际高吞吐高并发情况下Kafka系统出现topic和partition变化时,consumer性能的稳定性上有一定的帮助,有助于提高Kafka系统的稳定性。
2.2 对CPU使用率影响的对比
在系统稳定运行的某一时刻起,每隔5s记录一次C1和C2的CPU使用率,并以C1与C2的CPU使用率之和作为分区策略的CPU使用率。从记录数据开始的第20s时刻,将C0下线,在第40s时刻将C0上线。表2为60s时间段内三种分区策略的CPU使用率,图9为CPU使用率与时间分布图。
由表2和图9可知,C0的下线和上线RangeAssignor和RoundRobinAssignor两种策略都会进行重分区和负载均衡,这两种分区策略的CPU使用率迅速上升到一个峰值,说明系统开销较大,在实际应用过程中,系统容易出现负载不均衡的情况,很可能使某个consumer节点成为“热点”,出现节点宕机的情况。而StickyAssignor策略CPU使用率上升缓慢,开销变化比另两种分区策略较小,更晚的出现峰值。说明StickyAssignor策略在进行重分区和负载均衡时对系统的影响较小,较另外两种分区策略在负载均衡方面更有效,应用中出现“热点”节点的概率较低,增强了系统的健壮性。但StickyAssignor策略整体算法复杂度较另两种策略偏高,因此整体的CPU使用率偏高。
由以上实验可知,当Kafka系统对稳定要求较高时,StickyAssignor策略是个较为合适的选择。而对稳定性要求不高且考虑到系统开销时,选择Range-
Assignor和RoundRobinAssignor两种策略较为合适。其中RoundRobinAssignor策略较RangeAssignor策略在分区分配方面更为均衡,出现“热点”节点和负载不均衡的问题概率较低。
3 结束语
本文从consumer消费速率变化和CPU使用率两个方面对Kafka的三种分区策略作了对比分析。实验结果表明,在Kafka系统进行负载均衡过程中,StickyAssignor策略较另外两种策略有更好的稳定性,消费速率变化和CPU使用率没有过大的波动,降低了Kafka系统出现负载不均,和“过热”节点出现的概率,增强了系统的健壮性。RangeAssignor、RoundRobin-
Assignor两种策略相对StickyAssignor算法復杂度较低,因此在CPU使用率和系统开销方面较小。但在partition重分配过程中系统性能波动较大,容易造成负载不均衡的问题,降低系统稳定性。通过实验得出了三种策略对Kafka系统性能影响的特点,并提出了一些策略选择的建议。对于在实际生产应用Kafka系统进行分区策略选择时,能够起到一定的参考作用,特别是在高复杂性,高稳定性,高吞吐量应用场景下更具参考价值。
参考文献(References):
[1] 邸宇飞.基于Kafka的高速流量存储分发系统的研究与应用[D].北京邮电大学,2016.
[2] A distributed publish/subscribe notification service for pervasive environments[Ph.D.Thesis]. Zeidler,2004.
[3] 朱幼普.基于Kafka分布式能效管理平台的研究与应用[D].武汉邮电科学研究院,2018.
[4] Rabbit MQ in action. Videla A,Williams J J W,2012.
[5] Streaming Big Data Processing in Datacenter Clouds.Ranjan Rajiv. IEEE Cloud Computing,2014.
[6] 马建刚,黄涛,汪锦岭,徐罡,叶丹.面向大规模分布式计算发布订阅系统核心技术[J].软件学报,2006.1:134-147
[7] 马振刚.基于Kafka和Hadoop架构的工程研发数据挖掘[J].上海汽车,2020.6:12-16
[8] 倪超.从Paxos到Zookeeper分布式一致性原理[M].电子工业出版社,2015.
[9] 许红军.使用安全机制管理Kafka消息队列[J].网络安全和信息化,2020.5:134-139
[10] 王岩,王纯.一种基于Kafka的可靠的Consumer的设计方案[J].软件,2016.37(1):61-66
[11] 王郑合,王锋,邓辉,柳翠寅,张晓丽.一种优化的Kafka消费者/客户端负载均衡算法[J].计算机应用研究,2017.34(8):2306-2309
[12] 朱忠华.深入理解Kafka[M].电子工业出版社,2019.