0%

Kafka 分区策略

Kafka 分区策略


生产者分区选择配策略

生产者在将消息发送到某个Topic,需要经过拦截器、序列化器和分区器(Partitioner)的一系列作用之后才能发送到对应的Broker,在发往Broker之前是需要确定它所发往的分区。

生产端将消息发送给Broker之前,会将producer发送的数据封装成一个 ProducerRecord 对象。是否依赖分区器看partition字段有无指定。

是否依赖分区器看partition字段有无指定

  • 如果消息 ProducerRecord 指定了 partition 字段,那么就不需要分区器。
  • 如果消息 ProducerRecord 没有指定 partition 字段,那么就需要依赖分区器,根据key这个字段来计算partition的值。分区器的作用就是为消息分配分区。

Kafka 中提供的默认分区器是 DefaultPartitioner ,它实现了Partitioner接口(用户可以实现这个接口来自定义分区器)

用户可以通过实现kafka.producer.Partitioner接口实现自己的分区类(重载并实现partition方法),在生产端添加配置partitioner.class即可使用

  • 指明 partition 的情况下,直接将指明的值直接作为 partiton 值。
  • 没有指明 partition 值但有 key 的情况下,将 key 的 hash 值与 topic 的 partition 数进行取余得到 partition 值。
  • 既没有 partition 值又没有 key 值的情况下,第一次调用时随机生成一个整数(后面每次调用在这个整数上自增),将这个值与 topic 可用的 partition 总数取余得到 partition 值,也就是常说的 round-robin 算法。
  • 既没有 partition 值又指定了自定义的分区类,则按自定义分区类来得到 partition 值

消费者分区分配策略

消费者以组的名义订阅主题,主题有多个分区,消费者组中有多个消费者实例,同一时刻,一条消息只能被组中的一个消费者实例消费

  • 如果分区数大于或者等于组中的消费者实例数,一个消费者会负责多个分区
  • 如果分区数小于组中的消费者实例数,有些消费者将处于空闲状态并且无法接收消息

如果多个消费者负责同一个分区,那么就意味着两个消费者同时读取分区的消息,由于消费者自己可以控制读取消息的Offset,就有可能C1才读到2,而C1读到1,C1还没处理完,C2已经读到3了,这就相当于多线程读取同一个消息,会造成消息处理的重复,且不能保证消息的顺序。

在 Kafka 中存在着两种分区分配策略,通过 partition.assignment.strategy 来设置。

  • RangeAssignor 范围分区策略,也是默认模式。
  • RoundRobinAssignor 分配策略,轮询分区模式。
RangeAssignor

range (默认分配策略)对应的实现类是 org.apache.kafka.clients.consumer.RangeAssignor

  1. 将分区按数字顺序排行序,消费者按名称的字典序排序
  2. 用分区总数除以消费者总数。如果能够除尽,平均分配;若除不尽,则位于排序前面的消费者将多负责一个分区

假如现在有 10 个分区,3 个消费者,排序后的分区将会是p0~p9。消费者排序完之后将会是C1-0、C2-0、C3-0。通过 Partitions数 / Consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。

消费者 消费的分区
C1-0 消费 0、1、2、3分区
C2-0 消费 4、5、6分区
C3-0 消费 7、8、9分区

Range 范围分区的弊端:

如上只是针对 1 个 topic 而言,C1-0 消费者多消费1个分区影响不是很大。如果有 N 多个 topic,那么针对每个 topic,消费者 C1-0 都将多消费 1 个分区,topic越多,C1-0 消费的分区会比其他消费者明显多消费 N 个分区。这就是 Range 范围分区的一个很明显的弊端了.

RoundRobinAssignor

RoundRobin基于轮询算法,对应的实现类是 org.apache.kafka.clients.consumer.RoundRobinAssignor

  1. 将所有主题的分区组成TopicAndPartition列表
  2. 对TopicAndPartition列表按照hashCode进行排序某个topic
  3. 最后通过轮询算法来分配 partition 给到各个消费者

轮询分区分为如下两种情况:

  • 同一个 Consumer Group 内 Consumer 订阅信息相同
  • 同一个 Consumer Group 内 Consumer 订阅信息不相同
订阅信息相同

如果同一消费组内,所有的消费者订阅的消息都是相同的,那么 RoundRobin 策略的分区分配会是均匀的。

例如同一消费者组中,有 3 个消费者C0、C1和C2,都订阅了 2 个主题 t0 和 t1,并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所以分区可以标识为t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。

最终分区分配结果如下

消费者 消费的分区
C0 消费 t0p0、t1p0 分区
C1 消费 t0p1、t1p1 分区
C2 消费 t0p2、t1p2 分区
订阅信息不相同

同一消费者组内,所订阅的消息是不相同的,那么分区分配就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个 topic,那么在分配分区的时候,此消费者将不会分配到这个 topic 的任何分区。

例如同一消费者组中有3个消费者C0、C1、C2,他们共订阅了 3 个主题t0、t1、t2,这 3 个主题分别有 1、2、3 个分区(即t0有1个分区(p0),t1有2个分区(p0、p1),t2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2。然后消费者 C0 订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2

最终分区分配结果如下

消费者 消费的分区
C0 消费 t0p0 分区
C1 消费 t1p0 分区
C2 消费 t1p1、 t2p0、 t2p1、 t2p2 分区

参考链接