Kafka 分区分配策略介绍
# 一、基本介绍
- kafka 同一个 topic 可以设置多个分区,每个分区又可以分布在不同的节点,以实现高可用。同一个消费者组里面可以有多个消费者客户端,对于某个topic的某个分区在某个时刻只能被消费者组里的某一个消费者客户端消费,不能同时被同一个消费者组里面的多个消费者客户端消费。
- 同一个消费者组里的消费者客户端与同一个 topic 下的分区形成了多对多的关系,就需要某种分区分配策略将不同分区分配给不同的消费者客户端消费,下面将介绍客户端支持的常见的三种分配策略。
# 二、范围分配
- 范围分配策略(RangeAssignor)的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。
- 对于每一个主题,RangeAssignor 策略会将消费组内所有订阅这个主题的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
- 例如,消费组内有2个消费者C0和C1,主题有4个分区 p0、p1、p2、p3,那么对应的分配如下:
提示
平均分配
c0: p1 p2
c1: p3 p4
- 假设n=分区数/消费者数量,m=分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面的消费者每个分配n个分区。
- 例如,消费组内有2个消费者C0和C1,主题有3个分区 p0、p1、p2,那么对应的分配如下:
提示
非平均分配
c0: p1 p2
c1: p3
- 范围分配策略可能会导致分配并不均匀,有可能出现部分消费者过载的情况。
# 三、轮询分配
- 轮询分配策略(RoundRobinAssignor)的原理是将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序,然后通过轮询方式逐个将分区依次分配给每个消费者。如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor分配策略的分区分配会是均匀的。
- 例如,假设消费组中有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有3个分区,那么订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
提示
c0: t0p0、t0p2、t1p1
c1: t0p1、t1p0、t1p2
- 如果同一个消费组内的消费者订阅的信息是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能导致分区分配得不均匀。如果某个消费者没有订阅消费组内的某个主题,那么在分配分区的时候此消费者将分配不到这个主题的任何分区。
- 例如,假设消费组内有3个消费者(C0、C1和C2),它们共订阅了3个主题(t0、t1、t2),这3个主题分别有1、2、3个分区,即整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。消费者C0订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,那么最终的分配结果为:
提示
c0: t0p0
c1: t1p0
c2: t1p1、t2p0、t2p1、t2p2
- 范围分配策略也可能会导致分配并不均匀,有可能出现部分消费者过载的情况。
# 四、黏性分配
- 粘性分配策略(StickyAssignor)的原理是在分配分区时在保证均匀的情况下尽可能与上次分配的分区保持相同,这有助于节省一些主题分区从一个使用者移动到另一个使用者时的开销处理。
- 在重新分配期间,它将以以下方式执行重新分配:在新的分配中主题分区仍尽可能均匀地分布,并且主题分区尽可能地与先前分配的使用者保持在一起,优先保证均匀。
- 例如,3 个消费者,4 个主题,每个主题 2 个分区,对应的分区分布如下:
提示
C0: [t0p0, t1p1, t3p0]
C1: [t0p1, t2p0, t3p1]
C2: [t1p0, t2p1]
- 假设删除 C1 消费者,触发rebalance后,轮询分配的结果如下:
提示
C0: [t0p0, t1p0, t2p0, t3p0]
C2: [t0p1, t1p1, t2p1, t3p1]
- 而粘性分配将保留 C0、C2 的原有分区,并将 C1 的分区重新分配给 C0、C2,结果如下:
提示
C0 [t0p0, t1p1, t3p0, t2p0]
C2 [t1p0, t2p1, t0p1, t3p1]
- 再如,有 3 个消费者,3 个主题,分别有 1、2 和 3 个分区,C0 只订阅了 t0,C1 只订阅了 t0、t1,C2 订阅了 3 个主题,轮询分配如下:
提示
C0 [t0p0]
C1 [t1p0]
C2 [t1p1, t2p0, t2p1, t2p2]
- 粘性分配器的分配如下:
提示
C0 [t0p0]
C1 [t1p0, t1p1]
C2 [t2p0, t2p1, t2p2]
- 如果删除了 C0 消费者,循环分配器将得到以下分配:
提示
C1 [t0p0, t1p1]
C2 [t1p0, t2p0, t2p1, t2p2]
- 如果删除了 C0 消费者,粘性分配器将得到以下分配,相比于循环分配器更加均匀,能够避免部分消费者过载;
提示
C1 [t1p0, t1p1, t0p0]
C2 [t2p0, t2p1, t2p2]
# 五、自定义分配
- 自定义分配器,需要用户实现对应的接口函数,例如在 go 语言的 sarama 库中,需要用户实现接口即可;
# 六、结论
本文简单介绍了在一个消费者组中的多个消费者消费 kafka 多个分区时,分区分配给不同的消费者的 3 种分配策略,包括范围分配、轮询分配、粘性分配,这 3 中分配策略在 sarama 库中均已实现,在创建消费者客户端时直接选用即可。用户还可以根据 sarama 中分配策略的接口实现自定义的分配策略。
参考文章
[1] RangeAssignor (opens new window) [2] RoundRobinAssignor (opens new window) [3] StickyAssignor (opens new window)