背景
Kafka提供了消费者客户端参数partition.assignment.strategy
用来设置消费者与订阅主题之间的分区分配策略。默认情况下,此参数的值为:org.apache.kafka.clients.consumer.RangeAssignor
,即采用RangeAssignor分配策略。
分区策略
RangeAssignor
RoundRobinAssignor
StickyAssignor
RangeAssignor
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
分配举例
假设消费组内有2个消费者C1和C2,都订阅了主题t0和t1,并且每个主题都有4个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3。最终的分配结果为:
消费者C1:t0p0、t0p1、t1p0、t1p1
消费者C2:t0p2、t0p3、t1p2、t1p3
假设上面例子中2个主题都只有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
消费者C1:t0p0、t0p1、t1p0、t1p1
消费者C2:t0p2、t1p2
可看出RangeAssignor策略在主题分区数不整除的情况下会造成消费数分配得到的分区数不均.
RoundRobinAssignor
RoundRobinAssignor策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者。
如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor策略的分区分配会是均匀的。如果同一个消费组内的消费者所订阅的信息是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能会导致分区分配的不均匀。
分配举例
假设消费组中有2个消费者C1和C2,都订阅了主题t0和t1,并且每个主题都有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
消费者C1:t0p0、t0p2、t1p1
消费者C2:t0p1、t1p0、t1p2
假设消费组内有3个消费者C0、C1和C2,它们共订阅了3个主题:t0、t1、t2,这3个主题分别有1、2、3个分区,即整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。那么最终的分配结果为:
消费者C0:t0p0
消费者C1:t1p0
消费者C2:t1p1、t2p0、t2p1、t2p2
可以看到RoundRobinAssignor策略也不是十分完美,这样分配其实并不是最优解,因为完全可以将分区t1p1分配给消费者C1。
StickyAssignor
Kafka从0.11.x版本开始引入这种分配策略,它主要有两个目的:
分区的分配要尽可能的均匀;
分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。就是在发生重平衡时优先保障原来已分配的分区尽量不变,再根据变化的分区进行重新分配
分配举例
假设消费组内有3个消费者:C1、C2和C3,它们都订阅了4个主题:t0、t1、t2、t3,并且每个主题有2个分区,也就是说整个消费组订阅了t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1这8个分区。最终的分配结果如下:
消费者C1:t0p0、t1p1、t3p0
消费者C2:t0p1、t2p0、t3p1
消费者C3:t1p0、t2p1
假设此时消费者C2脱离了消费组,那么消费组就会执行再平衡操作,进而消费分区会重新分配。使用StickyAssignor策略的分配结果为:
消费者C1:t0p0、t1p1、t3p0、t2p0
消费者C3:t1p0、t2p1、t0p1、t3p1
参考
http://honeypps.com/mq/kafka-partitions-allocation-strategy-1-range-assignor/




