1.RangeAssignor
按照 Topic 的维度进行分配的
按照Topic对应的每个分区平均的按照范围区段分配给Consumer实例
1.1.源码分析
1 | public class RangeAssignor extends AbstractPartitionAssignor { |
1.2.分配过程演示
针对以上源码我们可以拿一个topic为例,直观演示其分派过程:
1.topic的名字为(t1,t2,t3),每个topic都含有3个分区(t1p0,t1p1,t1p2)(t2p0,t2p1,t2p2)(t3p0,t3p1,t3p2),消费者2个(c0,c1)
2.根据源码 numPartitionsPerConsumer = numPartitionsForTopic / consumersForTopic.size()
计算出单个consumer所分配到的最小分区数量:numPartitionsPerConsumer
= 3/2 = 1
3.根据源码 consumersWithExtraPartition = numPartitionsForTopic % consumersForTopic.size()
计算平均分配以后的剩余partition数量 consumersWithExtraPartition
= 3 % 2 = 1
下面逐个消费者开始分配
c0 : (以t1为例)领取最小分区数量1,由于此时存在剩余额外分区,因此也从中领取一个,因此c0会被分配t1p0,t1p1两个分区
c1 : 由于c0已经领取了t1p0,t1p1,因此c1会从t1p2开始领取,同样,剩余分区consumersWithExtraPartition此时没有剩余的分区,这样,c1会被分配t1p2
t1,t2依次类推
最终,分区分配结果为:
c0:[t1p0,t1p1,t2p0,t2p1,t3p0,t3p1]
c1:[t1p2,t2p2,t3p2]
1.3.Range范围分区的弊端
如上,只是针对3个topic而言,c0消费者消费的分区已经比c1多消费3个了;如果有 N 多个 topic,那么针对每个topic,消费者c0都将多消费1个分区,topic越多,C0消费的分区会比其他消费者明显多消费N个分区,弊端显而易见!!!
2.RoundRobinAssignor(★重点掌握)
2.1.源码分析
1 | public class RoundRobinAssignor extends AbstractPartitionAssignor { |
从源码中可以得知RoundRobinAssignor
进行分区分配的时候不再像RangeAssignor
那样逐个topic
进行,即不是为某个topic
完成了分区分派以后,再进行下一个topic
的分区分派,而是首先将这个group
中的所有consumer
订阅的所有的topic-partition
按顺序展开,然后,依次对于每一个topic-partition
,在consumer
进行round robin
,为这个topic-partition
选择一个consumer。
2.2.分配过程演示
案例一
假如有两个topic:t0和t1,每个topic都有3个分区,分区编号从0开始,因此TopicPartition的List是:[t0p0,t0p1,t0p2,t1p0,t1p1,t1p2]
两个consumer C0,C1,他们都同时订阅了这两个topic
根据RoundRobinAssignor
进行分派:
t0p0 分配给C0
t0p1 分配给C1
t0p2 分配给C0
t1p0 分配给C1
t1p1 分配给C0
t1p1 分配给C1
最终分配结果:
C0: [t0p0, t0p2, t1p1]
C1: [t0p1, t1p0, t1p2]
案例二
假如有三个topic:t0、t1、t2,topic的分区数依次是1、2、3,分区编号从0开始,因此TopicPartition的List是:
[t0p0]
[t1p0,t1p1]
[t2p0,t2p1,t2p2]
有三个订阅者c0,c1,c2,c0订阅了t0,c1订阅了t0,t1,c2订阅了t0,t1,t2
根据RoundRobinAssignor
进行分派:
t0p0 分配给c0
t1p0 分配给c1
t1p1 分配给c2
t2p0 分配给c2 仅c2订阅了
t2p1 分配给c2 仅c2订阅了
t2p2 分配给c2 仅c2订阅了
最终分配结果:
C0: [t0p0]
C1: [t1p0]
C1: [t1p1, t2p0, t2p1, t2p2]
2.3.弊端
2.2.章节演示案例一的分配过程是比较理想的状态,同一消费组内,所有的消费者订阅的消息都是相同的,那么 RoundRobin
策略的分区分配会是均匀的。
如果同一消费者组内,所订阅的消息是不相同的(案例二),那么在执行分区分配的时候,就不是完全的轮询分配,有可能会导致分区分配的不均匀
可以看到RoundRobin
策略也并不是时分完美,这样分配其实并不是最优解,因为完全可以将分区 t1p1 分配给消费者 C1
所以,如果想要使用RoundRobin 轮询分区策略,必须满足如下两个条件:
- 每个消费者订阅的主题,必须是相同的
- 每个主题的消费者实例都是相同的。(即:案例一情况,才优先使用 RoundRobin 轮询分区策略)
3.StickyAssignor
@since kafka0.11
3.1.概述
- 主题分区的分配要尽可能的均匀
- 当Rebalance 发生时,尽可能保持上一次的分配方案
以上两个条件发生冲突时,第一个条件优先于第二个条件,使分配更加均匀
3.2.RoundRoubin 和 Sticky 两者的区别
3.2.1.Consumer订阅主题均匀的情况
从上面我们可以看出,初始状态各个Consumer 订阅是相同的时候,并且主题的分区数也是平均的时候,两种分配方案的结果是相同的。
但是当Rebalance 发生时,可能就会不太相同了,加入上面的C1 发生了离组操作,此时分别会有下面的 Rebalance 结果:
从上面Rebalance 后的结果可以看出,虽然两者最后分配都是均匀的,但是RoundRoubin 完全是重新分配了一遍,而Sticky 则是在原先的基础上达到了均匀的状态。
3.2.2.Consumer订阅主题不均匀的情况
从上面的订阅关系可以看出,Consumer 的订阅主题个数不均匀,并且各个主题的分区数也是不相同的。此时两种分配方案的结果有了较大的差异,但是相对来说Sticky 方式的分配相对来说是最合理的。下面我们看一下 C1 发生离组时,Rebalance 之后的分配结果:
从上面结果可以看出,RoundRoubin 的方案在Rebalance 之后造成了严重的分配倾斜。因此在生产上如果想要减少Rebalance 的开销,可以选用 Sticky 的分区分配策略
参考博客:
http://www.moguhu.com/article/detail?articleId=142
https://www.cnblogs.com/hzmark/p/sticky_assignor.html
- 本文作者: cll
- 本文链接: https://keeponcoding.github.io/2019/01/31/Kafka分区策略(源码分析)/
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!