Kafka重平衡机制
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kafka重平衡机制相关的知识,希望对你有一定的参考价值。
参考技术A ==Consumer Rebalance== 公平分配重平衡规定了如何让消费者组下的所有消费者来分配topic中的每一个分区。
重平衡的触发条件主要有三个:
消费者组内成员发生变更,这个变更包括了增加和减少消费者。注意这里的减少有很大的可能是被动的,就是某个消费者崩溃退出了
主题的分区数发生变更,kafka目前只支持增加分区,当增加的时候就会触发重平衡
订阅的主题发生变化,当消费者组使用正则表达式订阅主题,而恰好又新建了对应的主题,就会触发重平衡
重平衡过程中,消费者无法从kafka消费消息
kafka提供了三种重平衡分配策略
Range
这种分配是基于每个主题的分区分配,如果主题的分区分区不能平均分配给组内每个消费者,那么对该主题,某些消费者会被分配到额外的分区。我们来看看具体的例子。
举例 :目前有两个消费者C0和C1,两个主题t0和t1,每个主题三个分区,分别是t0p0,t0p1,t0p2,和t1p0,t1p1,t1p2。
那么分配情况会是:
C0:t0p0, t0p1, t1p0, t1p1
C1:t0p2, t1p2
我来大概解释一下,range这种模式,消费者被分配的单位是基于主题的,拿上面的例子来说,是主题t0的三个分区分配给2个消费者,t1三个分区分配给消费者。于是便会出现消费者c0分配到主题t0两个分区,以及t1两个分区的情况(一个主题有三个分区,三个分区无法匹配两个消费者,势必有一个消费者分到两个分区),而非每个消费者分配两个主题各三个分区。
RoundRobin
RoundRobin是基于全部主题的分区来进行分配的,同时这种分配也是kafka默认的rebalance分区策略。还是用刚刚的例子来看,
举例 :两个消费者C0和C1,两个主题t0和t1,每个主题三个分区,分别是t0p0,t0p1,t0p2,和t1p0,t1p1,t1p2。
由于是基于全部主题的分区,那么分配情况会是:
C0:t0p0, t0p1, t1p1
C1:t1p0, t0p2, t1p2
因为是基于全部主题的分区来平均分配给消费者,所以这种分配策略能更加均衡得分配分区给每一个消费者。
上面说的都是同一消费者组内消费组都订阅相同主题的情况。更复杂的情况是,同一组内的消费者订阅不同的主题,那么任然可能会导致分区不均衡的情况。
还是举例说明,有三个消费者C0,C1,C2 。三个主题t0,t1,t2,分别有1,2,3个分区 t0p0,t1p0,t1p1,t2p0,t2p1,t2p2。
其中,C0订阅t0,C1订阅t0,t1。C2订阅t0,t1,t2。最终订阅情况如下:
C0:t0p0
C1:t1p0
C2:t1p1,t2p0,t2p1,t2p2
这个结果乍一看有点迷,其实可以这样理解,按照序号顺序进行循环分配,t0只有一个分区,先碰到C0就分配给它了。t1有两个分区,被C1和C2订阅,那么会循环将两个分区分配出去,最后到t2,有三个分区,却只有C2订阅,那么就将三个分区分配给C2。
Sticky
Sticky分配策略是最新的也是最复杂的策略,其具体实现位于package org.apache.kafka.clients.consumer.StickyAssignor。
这种分配策略是在0.11.0才被提出来的,主要是为了一定程度解决上面提到的重平衡非要重新分配全部分区的问题。称为粘性分配策略 。
听名字就知道,主要是为了让目前的分配尽可能保持不变,只挪动尽可能少的分区来实现重平衡。
还是举例说明,有三个消费者C0,C1,C2 。三个主题t0,t1,t2,t3。每个主题各有两个分区, t0p0,t0p1,t1p0,t1p1,t2p0,t2p1,t3p0,t3p1。
现在订阅情况如下:
C0:t0p0,t1p1,t3p0
C1:t0p1,t2p0,t3p1
C2:t1p0,t2p1
假设现在C1挂掉了,如果是RoundRobin分配策略,那么会变成下面这样:
C0:t0p0,t1p0,t2p0,t3p0
C2:t0p1,t1p1,t2p1,t3p1
就是说它会全部重新打乱,再分配,而如何使用Sticky分配策略,会变成这样:
C0:t0p0,t1p1,t3p0,t2p0
C2:t1p0,t2p1,t0p1,t3p1
也就是说,尽可能保留了原来的分区情况,不去改变它,在这个基础上进行均衡分配,不过这个策略目前似乎还有些bug,所以实际使用也不多。
避免重平衡 尽可能避免消费者假死
session.timout.ms:设置为6s 控制心跳超时时间
heartbeat.interval.ms:设置2s 控制心跳发送频率 频率越高越不容易被误判,但也会消耗更多资源
max.poll.interval.ms:消费者poll数据后,需要一些处理,再进行拉取。如果两次拉取时间间隔超过这个参数设置的值,那么消费者就会被踢出消费者组。推荐为消费者处理消息最长耗时再加1分钟 ,默认值是5分钟
参考:https://www.cnblogs.com/listenfwind/p/12662968.html
kafka 再平衡机制
什么是再平衡
-- 所谓的再平衡,指的是在kafka consumer所订阅的topic发生变化时发生的一种分区重分配机制。一般有三种情况会触发再平衡:
- consumer group中的新增或删除某个consumer,导致其所消费的分区需要分配到组内其他的consumer上;
- consumer订阅的topic发生变化,比如订阅的topic采用的是正则表达式的形式,如test-*此时如果有一个新建了一个topic test-user,那么这个topic的所有分区也是会自动分配给当前的consumer的,此时就会发生再平衡;
- consumer所订阅的topic发生了新增分区的行为,那么新增的分区就会分配给当前的consumer,此时就会触发再平衡。
Kafka提供的再平衡策略主要有三种:Round Robin,Range和Sticky,默认使用Range。这三种分配策略的主要区别在于:
- Round Robin:会采用轮询的方式将当前所有的分区依次分配给所有的consumer;
- Range:首先会计算每个consumer可以消费的分区个数,然后按照顺序将指定个数范围的分区分配给各个consumer;
- Sticky:这种分区策略是最新版本中新增的一种策略,其主要实现了两个目的:
-- 将现有的分区尽可能均衡的分配给各个consumer,存在此目的的原因在于Round Robin和Range分配策略实际上都会导致某几个consumer承载过多的分区,从而导致消费压力不均衡;
-- 如果发生再平衡,那么在重新分配前的基础上会尽力保证当前未宕机的consumer所消费的分区不会被分配给其他的consumer上;
Round Robin
详细
-- 关于Round Robin重分配策略,其主要采用的是一种轮询的方式分配所有的分区,该策略主要实现的步骤如下。这里我们首先假设有三个topic:t0、t1和t2,这三个topic拥有的分区数分别为1、2和3,那么总共有六个分区,这六个分区分别为:t0-0、t1-0、t1-1、t2-0、t2-1和t2-2。这里假设我们有三个consumer:C0、C1和C2,它们订阅情况为:C0订阅t0,C1订阅t0和t1,C2订阅t0、t1和t2。那么这些分区的分配步骤如下:
- 首先将所有的partition和consumer按照字典序进行排序,所谓的字典序,就是按照其名称的字符串顺序,那么上面的六个分区和三个consumer排序之后分别为:
- 然后依次以按顺序轮询的方式将这六个分区分配给三个consumer,如果当前consumer没有订阅当前分区所在的topic,则轮询的判断下一个consumer:
-- 尝试将t0-0分配给C0,由于C0订阅了t0,因而可以分配成功;
-- 尝试将t1-0分配给C1,由于C1订阅了t1,因而可以分配成功;
-- 尝试将t1-1分配给C2,由于C2订阅了t1,因而可以分配成功;
-- 尝试将t2-0分配给C0,由于C0没有订阅t2,因而会轮询下一个consumer;
-- 尝试将t2-0分配给C1,由于C1没有订阅t2,因而会轮询下一个consumer;
-- 尝试将t2-0分配给C2,由于C2订阅了t2,因而可以分配成功;
-- 同理由于t2-1和t2-2所在的topic都没有被C0和C1所订阅,因而都不会分配成功,最终都会分配给C2。
-- 按照上述的步骤将所有的分区都分配完毕之后,最终分区的订阅情况如下:
-- 从上面的步骤分析可以看出,轮询的策略就是简单的将所有的partition和consumer按照字典序进行排序之后,然后依次将partition分配给各个consumer,如果当前的consumer没有订阅当前的partition,那么就会轮询下一个consumer,直至最终将所有的分区都分配完毕。但是从上面的分配结果可以看出,轮询的方式会导致每个consumer所承载的分区数量不一致,从而导致各个consumer压力不均一。
Range(默认策略)
-- 所谓的Range重分配策略,就是首先会计算各个consumer将会承载的分区数量,然后将指定数量的分区分配给该consumer。这里我们假设有两个consumer:C0和C1,两个topic:t0和t1,这两个topic分别都有三个分区,那么总共的分区有六个:t0-0、t0-1、t0-2、t1-0、t1-1和t1-2。那么Range分配策略将会按照如下步骤进行分区的分配:
- 需要注意的是,Range策略是按照topic依次进行分配的,比如我们以t0进行讲解,其首先会获取t0的所有分区:t0-0、t0-1和t0-2,以及所有订阅了该topic的consumer:C0和C1,并且会将这些分区和consumer按照字典序进行排序;
- 然后按照平均分配的方式计算每个consumer会得到多少个分区,如果没有除尽,则会将多出来的分区依次计算到前面几个consumer。比如这里是三个分区和两个consumer,那么每个consumer至少会得到1个分区,而3除以2后还余1,那么就会将多余的部分依次算到前面几个consumer,也就是这里的1会分配给第一个consumer,总结来说,那么C0将会从第0个分区开始,分配2个分区,而C1将会从第2个分区开始,分配1个分区;
- 同理,按照上面的步骤依次进行后面的topic的分配。
-- 最终上面六个分区的分配情况如下:
-- 可以看到,如果按照Range分区方式进行分配,其本质上是依次遍历每个topic,然后将这些topic的分区按照其所订阅的consumer数量进行平均的范围分配。这种方式从计算原理上就会导致排序在前面的consumer分配到更多的分区,从而导致各个consumer的压力不均衡。
Sticky
-- Sticky策略是新版本中新增的策略,顾名思义,这种策略会保证再分配时已经分配过的分区尽量保证其能够继续由当前正在消费的consumer继续消费,当然,前提是每个consumer所分配的分区数量都大致相同,这样能够保证每个consumer消费压力比较均衡。关于这种分配方式的分配策略,我们分两种情况进行讲解,即初始状态的分配和某个consumer宕机时的分配情况
初始分配
-- 初始状态分配的特点是,所有的分区都还未分配到任意一个consumer上。这里我们假设有三个consumer:C0、C1和C2,三个topic:t0、t1和t2,这三个topic分别有1、2和3个分区,那么总共的分区为:t0-0、t1-0、t1-1、t2-0、t2-1和t2-2。关于订阅情况,这里C0订阅了t0,C1订阅了t0和1,C2则订阅了t0、t1和t2。这里的分区分配规则如下:
- 首先将所有的分区进行排序,排序方式为:首先按照当前分区所分配的consumer数量从低到高进行排序,如果consumer数量相同,则按照分区的字典序进行排序。这里六个分区由于所在的topic的订阅情况各不相同,因而其排序结果如下:
- 然后将所有的consumer进行排序,其排序方式为:首先按照当前consumer已经分配的分区数量有小到大排序,如果两个consumer分配的分区数量相同,则会按照其名称的字典序进行排序。由于初始时,这三个consumer都没有分配任何分区,因而其排序结果即为其按照字典序进行排序的结果:
- 然后将各个分区依次遍历分配给各个consumer,首先需要注意的是,这里的遍历并不是C0分配完了再分配给C1,而是每次分配分区的时候都整个的对所有的consumer从头开始遍历分配,如果当前consumer没有订阅当前分区,则会遍历下一个consumer。然后需要注意的是,在整个分配的过程中,各个consumer所分配的分区数是动态变化的,而这种变化是会体现在各个consumer的排序上的,比如初始时C0是排在第一个的,此时如果分配了一个分区给C0,那么C0就会排到最后,因为其拥有的分区数是最多的。上面的六个分区整体的分配流程如下:
a. 首先将t2-0尝试分配给C0,由于C0没有订阅t2,因而分配不成功,继续轮询下一个consumer;
b. 然后将t2-0尝试分配给C1,由于C1没有订阅t2,因而分配不成功,继续轮询下一个consumer;
c. 接着将t2-0尝试分配给C2,由于C2订阅了t2,因而分配成功,此时由于C2分配的分区数发生变化,各个consumer变更后的排序结果为:
d. 接下来的t2-1和t2-2,由于也只有C2订阅了t2,因而其最终还是会分配给C2,最终在t2-0、t2-1和t2-2分配完之后,各个consumer的排序以及其分区分配情况如下:
e. 接着继续分配t1-0,首先尝试将其分配给C0,由于C0没有订阅t1,因而分配不成功,继续轮询下一个consumer;
f. 然后尝试将t1-0分配给C1,由于C1订阅了t1,因而分配成功,此时各个consumer以及其分配的分区情况如下:
g. 同理,接下来会分配t1-1,虽然C1和C2都订阅了t1,但是由于C1排在C2前面,因而该分区会分配给C1,即:
h. 最后,尝试将t0-0分配给C0,由于C0订阅了t0,因而分配成功,最终的分配结果为:
-- 上面的分配过程中,需要始终注意的是,虽然示例中的consumer顺序始终没有变化,但这是由于各个分区分配之后正好每个consumer所分配的分区数量的排序结果与初始状态一致。这里读者也可以比较一下这种分配方式与前面讲解的Round Robin进行对比,可以很明显的发现,Sticky重分配策略分配得更加均匀一些。
模拟consumer宕机
-- 由于前一个示例中最终的分区分配方式模拟宕机的情形比较简单,因而我们使用另一种订阅策略。这里我们的示例的consumer有三个:C0、C1和C2,topic有四个:t0、t1、t2和t3,每个topic都有两个分区,那么总的分区有:t0-0、t0-1、t1-0、t1-1、t2-0、t2-1、t3-0和t3-1。这里的订阅情况为三个consumer订阅所有的主题,那么如果按照Sticky的分区分配策略,初始状态时,分配情况如下,读者可以按照前一示例讲解的方式进行推算:
-- 这里我们假设在消费的过程中,C1发生了宕机,此时就会发生再平衡,而根据Sticky策略,其再分配步骤如下:
- 首先会将宕机之后未分配的分区进行排序,排序方式为:首先按照分区所拥有的consumer数量从低到高进行排序,如果consumer数量相同,则按照分区的字典序进行排序。这里需要注意的是,由于只有C1宕机,因而未分配的分区为:t0-1、t2-0和t3-1,排序之后的结果为:
- 然后将所有的consumer进行排序,排序方式为:首先将consumer按照其所拥有的consumer数量从小到大排序,如果数量相同,则按照consumer名称的字典序进行排序,排序结果如下:
- 接着依次遍历各个分区,将其分配给各个consumer,需要注意的是,在分配的过程中,consumer所分配的分区数量是在变化的,而这种变化是会反应在consumer的排序上的:
a. 首先尝试将t0-1分配给C2,由于C2订阅了t0,因而可以分配成功,此时consumer排序和分区分配情况如下,需要注意的是,虽然分配之后,C2和C0的分区数量相同,但是由于按照字典序,C0在C2前面,因而排序情况还是会发生变化:
b. 然后尝试将t2-0分配给C0,由于C0订阅了t2,因而分配可以成功,此时consumer排序和分区分配情况如下:
c. 最后尝试分配t3-1给C2,由于C2订阅了t3,因而分配可以成功,此时consumer排序与分区分配情况如下:
-- 在上面的分区分配过程中,我们可以看到,由于分区的不断分配,各个consumer所拥有的分区数量也在不断变化,因而其排序情况也在变化,但是最终可以看到,各个分区是均匀的分配到各个consumer的,并且还保证了当前consumer已经消费的分区是不会分配到其他的consumer上的。
以上是关于Kafka重平衡机制的主要内容,如果未能解决你的问题,请参考以下文章