1.Kafka 分区分配策略
在 Kafka 内部存在两种默认的分区分配策略:Range 和 RoundRobin。当以下事件发 生时,Kafka 将会进行一次分区分配:
同一个 Consumer Group 内新增消费者
消费者离开当前所属的 Consumer Group,包括 shuts down 或 crashes
订阅的主题新增分区:将分区的所有权从一个消费者移到另一个消费者称为重新平衡(rebalance),如何 rebalance 就涉及到下面提到的分区分配策略。下面我们将详细介绍 Kafka 内置的两种分区 分配策略。本文假设我们有个名为 T1 的主题,其包含了 10 个分区,然后我们有两个消费 者(C1,C2)来消费这 10 个分区里面的数据,而且 C1 的 num.streams = 1,C2 的 num.streams = 2。
(1) Range strategy
Range 策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并 对消费者按照字母顺序进行排序。在我们的例子里面,排完序的分区将会是 0, 1, 2, 3, 4, 5, 6, 7, 8, 9;消费者线程排完序将会是 C1-0, C2-0, C2-1。然后将 partitions 的个数除于消费者线程 的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多 消费一个分区。
在我们的例子里面,我们有 10 个分区,3 个消费者线程, 10 / 3 = 3,而且除不尽,那 么消费者线程 C1-0 将会多消费一个分区,所以最后分区分配的结果看起来是这样的:
C1-0 将消费 0, 1, 2, 3 分区
C2-0 将消费 4, 5, 6 分区
C2-1 将消费 7, 8, 9 分区
假如我们有 11 个分区,那么最后分区分配的结果看起来是这样的: C1-0 将消费 0, 1, 2, 3 分区
C2-0 将消费 4, 5, 6, 7 分区
C2-1 将消费 8, 9, 10 分区
假如我们有 2 个主题(T1 和 T2),分别有 10 个分区,那么最后分区分配的结果看起来是这样的:
C1-0 将消费 T1 主题的 0, 1, 2, 3 分区以及 T2 主题的 0, 1, 2, 3 分区
C2-0 将消费 T1 主题的 4, 5, 6 分区以及 T2 主题的 4, 5, 6 分区
C2-1 将消费 T1 主题的 7, 8, 9 分区以及 T2 主题的 7, 8, 9 分区
可以看出,C1-0 消费者线程比其他消费者线程多消费了 2 个分区,这就是 Range strategy 的一个很明显的弊端。
(2) RoundRobin strategy
使用 RoundRobin 策略有两个前提条件必须满足:
同一个 Consumer Group 里面的所有消费者的 num.streams 必须相等;
每个消费者订阅的主题必须相同。
所以这里假设前面提到的 2 个消费者的 num.streams = 2。RoundRobin 策略的工作原理: 将所有主题的分区组成 TopicAndPartition 列表,然后对 TopicAndPartition 列表按照 hashCode 进行排序,这里文字可能说不清,看下面的代码应该会明白:
val allTopicPartitions = ctx.partitionsForTopic.flatMap { case(topic, partitions) => info("Consumer %s rebalancing the following partitions for topic %s: %s"
.format(ctx.consumerId, topic, partitions)) partitions.map(partition => {
TopicAndPartition(topic, partition) })
}.toSeq.sortWith((topicPartition1, topicPartition2) => { /*
* Randomize the order by taking the hashcode to reduce the likelihood of all partitions of a given topic ending
* up on one consumer (if it has a high enough stream count).
*/
topicPartition1.toString.hashCode < topicPartition2.toString.hashCode
})
最后按照 round-robin 风格将分区分别分配给不同的消费者线程。
在我们的例子里面,假如按照 hashCode 排序完的 topic-partitions 组依次为 T1-5, T1-3, T1-0, T1-8, T1-2, T1-1, T1-4, T1-7, T1-6, T1-9,我们的消费者线程排序为 C1-0, C1-1, C2-0, C2-1,最后分区分配的结果为:
C1-0 将消费 T1-5, T1-2, T1-6 分区;
C1-1 将消费 T1-3, T1-1, T1-9 分区;
C2-0 将消费 T1-0, T1-4 分区;
C2-1 将消费 T1-8, T1-7 分区;