在上一篇 kafka topic消息分配partition规则(Java源码) 我们对生产者产生的消息分配partition规则进行了分析,那么本章我们来看看消费者是怎么样分配partition的。
kafka 为了保证同一类型的消息顺序性(FIFO),一个partition只能被同一组的一个consumer绑定消费,不同组的consumer可以绑定同一个partition进行重复消费。但是一个consumer可以绑定多个partition(哈哈不知道这里用绑定是否合适)。用个例子解释一下:有一个topic T1 有4个partition;有一个消费组 G1,在G1创建一个consumer C1,这时C1就会消费T1的4个partition:
有两个消费组时:
一个消费组只有一个消费者时很容易理解,那么一个消费组有多个消费怎么分配呢(一个组的消费者个数最好不要比partition个数多,否则多的消费者就是一种浪费),本章重点来了,实现org.apache.kafka.clients.consumer.internals.PartitionAssignor这个接口就可以制定partition对消费者的分配了。kafka已经实现了RoundRobinAssignor和RangeAssignor两种。
类关系:
RoundRobinAssignor
该策略把主题的所有分区逐个分配给消费者。如果使用 RoundRobinAssignor 策略来给消费者 C1 和消费者 C2 分配分区,那么消费者 C1 将分到主题 T1 的分区 0 和分区 2 以及主题 T2 的分区 1,消费者 C2 将分配到主题 T1 的分区 1 以及主题 T2 的分区 0 和分区 2。一般来说,如果所有消费者都订阅相同的主题(这种情况很常见),RoundRobin 策略会给所有消费者分配相同数量的分区(或最多就差一个分区) 。
以上截图是轮询策略的主要代码,1 中创建了CircularIterator用来可以循环遍历的对象,在 2 中就对所有消费者进行了轮询分配partition。
RangeAssignor:
该策略会把主题的若干个连续的分区分配给消费者(kafka默认用该策略)。假设消费者 C1 和消费者 C2 同时 订阅了主题 T1 和主题 T2,并且每个主题有 3 个分区。那么消费者 C1 有可能分配到这 两个主题的分区 0 和分区 1,而消费者 C2 分配到这两个主题的分区 2。因为每个主题 拥有奇数个分区,而分配是在主题内独立完成的,第一个消费者最后分配到比第二个消 费者更多的分区。只要使用了 RangeAssignor 策略,而且分区数量无法被消费者数量整除,就会 出现这种情况。
以上截图是RangeAssignor策略的主要代码:
1 中用话题的分区总数除以该话题的消费者数量(取整数);
2 中用话题的分区总数对该话题的消费者数量取模;
345 计算某个消费者对应的partition区间。
我们可以通过配置partition.assignment.strategy参数指定用哪个策略。