年初的时候用搭建过一个数据处理系统,mq用的kafka,当时对kafka的分区策略不明确,用的默认策略,即RangeAssigor,但我并不知道具体的分区消费逻辑。这几天趁着架构组向下推广kafka, 我研究了具体的分区原理。
最新的kafka有三种分区策略,分别是RangeAssigor、RoundRobinAssignor、StickyAssignor,默认的策略是RangeAssigor,客户端可以指定使用某种策略,那这三种策略的原理、应用场景是什么?
A组: 两个主题T1 、 T2 ,每个主题配备四个分区,分别为P1 、P2 、P3 、 P4
B组: 两个主题T1、T2, 每个主题配备三个分区,分别为P1 、P2 、P3
C组: 一个主题T1,每个主题配备四个分区,分别为P1、P2、P3、P4
D组: 三个主题T1、T2、T3, 每个主题分别有 1 、 2、 3个分区
E组:四个主题T1、T2、T3、T4,每个主题分别有俩分区
由于在kafka中,一个消费组里的消费者可以订阅多个topic,每个topic会包含多个分区,但默认情况下一个分区只能被一个消费组下面的一个消费者消费,并且如果消费者没有订阅某个主题,那么该消费者不会分配到这个主题下的分区。
那么为什么我们要分区? 分区对于 Kafka 集群的好处是:实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率。
所以我们把主题与分区进行排列组合一下,即:
A组: T1P1、T1P2 、 T1P3、 T1P4、 T2P1、 T2P2、 T2P3、 T2P4八种
B组: T1P1、T1P2 、 T1P3、 T2P1、 T2P2、 T2P3 六种
C组: T1P1、 T1P2、 T1P3 、T1P4 四种
D组:T1P1 、T2P1、 T2P2、T3P1 、 T3P2、 T3P3 六种
E组:T1P1、T1P2、T2P1、T2P2、T3P1、T3P2、T4P1、T4P2
RangeAssigor分区策略:RangeAssigor策略是对每个topic单独进行分配的,原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
同一消费组订阅同一主题
同一消费组消费订阅不同主题
此时看,由于每个主题的分区个数都可以被同一消费组的消费者个数整除,所以此时看着分配的还听均匀哈,那如果不能被整除的话,由会如何分配呢?
此时再看,消费者承担的分区就不均匀了,kafka里有很多个这样的主题会发生什么呢?答案是消费者C1承担的分区过多, 负载不均衡,会造成挤压,消费不及时,可见这种消费有问题。
RoundRobinAssignor分区策略:RoundRobinAssignor策略原理是将所有分区都按照字典顺序排序,然后通过轮询的方式均匀的分配给消费组内的每个消费者,我们可以拿此策略与nginx的roundrobin轮询策略类别,原理上是一致的。
由于是将所有主题与分区的排列组合按照字典顺序排序,所以我们无需考虑统一消费组内订阅几个主题的情况
我们可以看到,RoundRobinAssignor分区策略解决了RangeAssigor的问题,遇到分区个数不能被消费者个数争取的情况,轮询策略分配的会更加均匀,从而达到避免过载问题。、
这样分配看起来依旧不错,无伤大雅
那么你看出问题了吗,此种情况下,分区又不均匀了,C3挂上的分区过多,负载变高,一般情况下咱们用不到同一个消费组如此订阅主题,这种比较高级。
StickyAssignor分区策略:Kafka从0.11.x版本开始引入这种分配策略,属于对roundRobin的一种优化,他有俩目的,一个是为了更均匀的分配分区,另一个就是分区的分配尽可能的与上次分配的保持相同,这点可能这么看不理解,下面我会具体举例给解释一下
有没有很感动?这不就是roundRobin策略吗?你看结果都一样,你是不是耍我?别急,我说了这是roundRobin的一种优化嘛,如果此时C2突然肚子痛挂了呢?会引起组内rebalance,重新分配分区,分配结果如下
此时还不直观,我们再把roundRobin策略下C2挂了的情况触发rebalance后分配结果列一下
对比StickyAssignor、roundRobinAssignor两种策略rebalance后分配结果,你发现了什么?roundRobinAssignor实际上是在C2挂了之后,重新将8个分区按规则均匀分配到C1、C3上,但是StickyAssignor缺没有重新分配,而是在原有C1、C3的已有分区的基础上,继续分配C2的分区,并且还实现了均匀分配,保证负载均衡,现在觉得呢,是不是StickyAssignor策略更好一些?你可能要问,就为了个避免全部重新分配?这里牵扯出rebalance后,消费者重新消费新分区的时耗问题,尽可能少的分配,才会更快的拉起服务。
目前咱们引入的kafka, 分配策略还处在默认策略即RangeAssigor, 这个分业务,目前看,咱们的业务几乎都是一个消费组订阅同一个主题,并且架构组提供的扩展也是,同一消费组仅支持消费一个主题,如果需要消费多个主题,那得改造一下。理论上默认策略够用,也不会引起负载问题。而如果我们有一个消费组订阅不同主题的情况,我建议直接使用StickyAssignor策略,因为他在消费者或者分区挂了时,rebalance更均匀,更友好。最后策略可以由客户端指定,不是说咱放着好的策略不用,这得根据场景选。
这里附上kafka官方文档,大家有兴许可以继续深入 https://kafka.apache.org/24/javadoc/org/apache/kafka/clients/consumer/