概述
kakfa的topic有多个partition,而消费端是以消费组为单元进行分区的消息,那么如何将一个topic下面的partition合理的分配给消费者中的消费者。Kafka有几种分配策略
RangeAssignor
RangeAssignor 策略是基于每个topic之上的,对于每个topic而言,kafka 列出可用的分区,对于每个topic,我们按数字顺序排列可用分区,以消费者的名称的词典顺序列出消费者。然后,我们将分区数量除以消费者总数,以确定分配给每个消费者的分区数量。如果它不均匀地划分,那么前几个消费者将有一个额外的分区。
如下图,有topic t1 和 消费组,t1 有四个分区,消费组有三个消费者。
分区依次为:t1.p0,t1.p1,t1.p2,t1.p3
假设N = 分区数,M=消费者数量
N/M = 4/3 = 1
N%M = 4%3 = 1
分配:前(N%M)个消费者,分配N 1(1 1) = 2 个分区,其余的消费者分配 N 个
所以,最终的分配结果就是:
c0: t1.p0,t1.p1
c1: t1.p2
c2:t1.p3
如果同时订阅了多个主题,则按每个主题分配后,会出现不均衡的情况,如下图,有两个主题t1和t2
分配后的结果为:
c0: t1.p0, t1.p1, t2.p0,t2.p1
c1: t1.p2, t2.p2
c2: t1.p3, t2,p3
RoundRobinAssignor
循环赋值器列出所有可用分区和所有可用使用者。然后,它继续执行从分区到使用者的循环分配。如果所有使用者实例的订阅都相同,则分区将均匀分布。(即,所有使用者的分区所有权计数都将在正好1的增量范围内。)
如上图,假设有两个消费者C0和C1,两个主题t0和t1,每个主题有3个分区,从而得到分区t0p0、t0p1、t0p2、t1p0、t1p1和t1p2。
分配结果为:
- C0: [t0p0, t0p2, t1p1]
- C1: [t0p1, t1p0, 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]
由于引入了静态成员身份,我们可以利用group.instance.id使分配行为更具粘性。例如,我们有三个消费者,分配了member.id C0、C1、C2、两个主题t0和t1,每个主题有3个分区,从而产生了分区t0p0、t0p1、t0p2、t1p0、t1p1和t1p2。我们选择根据ephemeral member.id执行排序顺序。如下图:
分配结果为:
C0: [t0p0, t1p0]
C1: [t0p1, t1p1]
C2: [t0p2, t1p2] 组协调员将尝试向使用者分配新的
在一次重新均衡后,组协调员将尝试向使用者分配新的memberid,例如C0->C5 C1->C3,C2->C4。 任务可以完全转移到, 如下图:
C4(是 C2):[t0p1,t1p1](在was[t0p2,t1p2]之前)
C3(是C1):[t0p0,t1p0](之前是[t0p1,t1p1])
C5(是C0):[t0p2,t1p2](之前是[t0p0,t1p0])
这个问题可以通过引入静态成员来缓解。 消费者将拥有单独的实例 ID I1、I2、I3。 只要
1. 跨代成员数量保持不变 2. 静态成员的身份跨代保持不变 3. 任何成员的订阅模式都不会改变
分配结果一直会是:
I0: [t0p0, t1p0]
I1: [t0p1, t1p1]
I2: [t0p2, t1p2]
StickyAssignor
粘性分配器有两个目的。首先,它保证分配尽可能平衡,它有两个目的:
- 分配给消费者的topic partition个数最多相差1个;或者
- 主题分区比其他消费者少 2 的每个消费者无法将这些主题分区中的任何一个转移到它。
其次,当发生重新分配时,它尽可能多地保留现有分配。当主题分区从一个消费者移动到另一个消费者时,这有助于节省一些开销处理。
重新开始它可以通过将分区尽可能均匀地分布在消费者身上来工作。尽管这听起来与循环分配器的工作方式相似,但下面的第二个示例表明事实并非如此。在重新分配期间,它将以这样一种方式执行重新分配,即在新分配中
- 主题分区仍然尽可能均匀地分布
- 主题分区尽可能地保留在其先前分配的消费者中。
当然,上面的第一个目标优先于第二个目标。
例 1.假设有 3 个消费者C0, C1, C2, 4 个主题t0, t1, t2, t3, , 每个主题有 2 个分区,从而产生分区t0p0, t0p1, t1p0, t1p1, t2p0, t2p1, t3p0, t3p1。每个消费者都订阅了所有三个主题。具有粘性和循环分配器的分配将是:
- C0: [t0p0, t1p1, t3p0]
- C1: [t0p1, t2p0, t3p1]
- C2: [t1p0, t2p1]
现在,让我们假设C1被删除并且重新分配即将发生。
循环分配器将产生:
- C0: [t0p0, t1p0, t2p0, t3p0]
- C2: [t0p1, t1p1, t2p1, t3p1]
而粘性分配器会导致:
- C0 [t0p0, t1p1, t3p0, t2p0]
- C2 [t1p0, t2p1, t0p1, t3p1]
保留所有以前的分配(与循环分配器不同)
示例 2.有 3 个消费者C0、C1、C2和 3 个主题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]
这不像粘性分配器建议的分配那样平衡:
- C0 [t0p0]
- C1 [t1p0, t1p1]
- C2 [t2p0, t2p1, t2p2]
现在如果消费者C0被移除,这两个分配器将产生以下分配。
循环(保留 3 个分区分配):
- C1 [t0p0, t1p1]
- C2 [t1p0, t2p0, t2p1, t2p2]
粘性(保留 5 个分区分配):
- C1 [t1p0, t1p1, t0p0]
- C2 [t2p0, t2p1, t2p2]