kafka分区分配策略 参数:
Kafka提供了消费者客户端参数partition.assignment.strategy来设置消费者与订阅主题之间的分区分配策略。
默认情况下,此参数的值为 org.apache.kafka.clients.consumer.RangeAssignor,即采用RangeAssignor分配策略。除此之外,Kafka还提供了另外两种分配策略:RoundRobinAssignor 和 StickyAssignor。
RangeAssignor 分配策略
RangeAssignor 分配策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
对于每一个主题,RangeAssignor策略会将消费组内所有订阅这个主题的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
假设n=分区数/消费者数量,m=分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面的(消费者数量-m)个消费者每个分配n个分区。前m个消费者,相当于是除不尽的余数,特殊分配多一个分区。
假设消费组内有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有3个分区,
那么订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。
最终的分配结果为:
消费者c0: t0p0、t0p1、t1p0、t1p1
消费者c1: t0p2、t1p2
分区数为3,消费者数量为2,n=3/2 =1,m=3%2=1。前m个消费者每个分配n+1个分区,后面的(消费者数量-m)个消费者每个分配n个分区。
可以明显地看到这样的分配并不均匀,如果将类似的情形扩大,则有可能出现部分消费者过载的情况。
RoundRobinAssignor分配策略
RoundRobinAssignor分配策略的原理是将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序,然后通过轮询方式逐个将分区依次分配给每个消费者。
假设消费组内有3个消费者(C0、C1和C2),它们共订阅了3个主题(t0、t1、t2),这3个主题分别有1、2、3个分区,即整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。
具体而言,消费者C0订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,
那么最终的分配结果为:
消费者c0: t0p0
消费者c1: t1p0
消费者c2: t1p1, t2p0,t2p1, t2p2
RoundRobinAssignor策略也不是十分完美,这样分配其实并不是最优解,因为完全可以将分区t1p1分配给消费者C1。
StickyAssignor 分配策略
主要有两个目的: (1)分区的分配要尽可能均匀。 (2)分区的分配尽可能与上次分配的保持相同。
如同其名称中的“sticky”一样,让分配策略具备一定的“黏性”,尽可能地让前后两次分配相同,进而减少系统资源的损耗及其他异常情况的发生。
如果发生分区重分配,那么对于同一个分区而言,有可能之前的消费者和新指派的消费者不是同一个,之前消费者进行到一半的处理还要在新指派的消费者中再次复现一遍,这显然很浪费系统资源。StickyAssignor 分配策略如同其名称中的“sticky”一样,让分配策略具备一定的“黏性”,尽可能地让前后两次分配相同,进而减少系统资源的损耗及其他异常情况的发生。
自定义分区分配策略
不仅可以任意选用Kafka提供的3种分配策略,还可以自定义分配策略来实现更多可选的功能。
自定义的分配策略必须要实现 org.apache.kafka.clients.consumer.internals.PartitionAssignor接口。
资料来源:
《深入理解Kafka:核心设计与实践原理》