基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Kafka Producer 的分区路由策略(Partitioner)有哪些?

知识点图片

在 Kafka 中,生产者(Producer)在发送消息时,必须决定将消息发送到主题(Topic)的哪一个分区(Partition)。这个决定由 分区路由策略(Partitioner) 来完成。

Kafka 提供了多种内置的分区策略,并且允许用户自定义策略。以下是 Kafka Producer 支持的主要分区路由策略:


1. 默认分区策略(Default Partitioner)

这是 Kafka Producer 默认使用的策略。注意:在 Kafka 2.4 版本前后,默认策略发生了重大的改变。

A. 如果消息指定了 Key(按 Key 哈希)

  • 适用版本: 所有版本
  • 路由逻辑: 如果 ProducerRecord 中指定了 key,Kafka 会对该 key 进行哈希计算(通常使用 murmur2 算法),然后将哈希值对总分区数取模:hash(key) % num_partitions
  • 特点: 保证了相同 Key 的消息一定会被路由到同一个分区,从而保证了该 Key 对应消息的局部顺序性
  • 注意:如果主题增加了分区,相同 Key 可能会被路由到新的分区。

B. 如果消息没有指定 Key(无 Key 时的策略)

  • Kafka 2.4 之前的版本(Round-Robin 轮询):
    如果没有指定 key,生产者会采用轮询(Round-Robin)的方式,将消息依次分配到各个可用分区中。
    缺点: 这种方式会导致消息极其分散,无法有效填满一个批次(Batch),导致网络请求增多、吞吐量下降。
  • Kafka 2.4 及之后的版本(Sticky 粘性分区策略):
    为了解决轮询带来的批次效率低下的问题,Kafka 引入了 粘性分区器(Sticky Partitioner) (KIP-480)。
    路由逻辑: 生产者会随机选择一个分区,并“粘”在这个分区上,直到该分区的批次(Batch)被填满(达到 batch.size)或者等待时间耗尽(达到 linger.ms)。批次发送出去后,再随机选择下一个分区进行“粘滞”。
    优点: 极大地提升了 Batching(批处理)效率,降低了延迟,提高了吞吐量,同时在宏观上依然保持了数据的均匀分布。

2. 轮询分区策略(RoundRobinPartitioner)

  • 配置方式: partitioner.class=org.apache.kafka.clients.producer.RoundRobinPartitioner
  • 路由逻辑: 无论消息是否有 key,都严格按照轮询的方式(0, 1, 2, 0, 1, 2...)将消息依次发送到所有的分区中。
  • 适用场景: 极少使用。只有当你完全不在乎消息顺序,且出于某种极其特殊的业务原因要求单条消息绝对均匀分布,而不考虑网络批处理性能时才会使用。

3. 统一粘性分区策略(UniformStickyPartitioner)

  • 配置方式: partitioner.class=org.apache.kafka.clients.producer.UniformStickyPartitioner
  • 路由逻辑: 无论消息是否有 key,都忽略 Key,强制采用“粘性分区”策略(即填满一个批次再换下一个分区)。
  • 适用场景: 消息带有关联的 Key(例如想要在消费端记录这个 Key),但在 Kafka 路由阶段你不需要保证相同 Key 的顺序,只追求最高的吞吐量和最低的延迟。

4. 指定分区发送(Direct/Manual Partitioning)

  • 路由逻辑: 开发者在构建 ProducerRecord 对象时,直接硬编码指定 partition 的编号。
    java
    // 指定发送到编号为 2 的分区
    ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", 2, "my-key", "my-value");
  • 特点: 优先级最高。只要代码里指定了分区,Kafka 就会忽略所有的 Partitioner 策略,直接发往该分区。
  • 适用场景: 开发者自己维护路由逻辑,或者有极其严格的特定消息发往特定分区的需求。

5. 自定义分区策略(Custom Partitioner)

如果内置的策略不能满足业务需求,Kafka 允许开发者编写自定义的分区规则。

  • 实现方式: 实现 org.apache.kafka.clients.producer.Partitioner 接口,重写 partition() 方法。
  • 配置方式: partitioner.class=com.yourcompany.MyCustomPartitioner
  • 常见业务场景:
    1. 处理数据倾斜(Data Skew): 比如某个 Key 的数据量特别大,按普通哈希会压垮单个分区。可以在自定义策略中把特定的热点 Key 散列到多个特定分区,其他的 Key 走普通哈希。
    2. VIP/多租户隔离: 根据消息 Key 或者 Value 中的某个字段,判断如果是 VIP 客户的数据,强制路由到拥有高性能磁盘的分区 0 和 1;普通用户数据路由到分区 2、3、4。
    3. 机房/地域就近路由: 根据生产者所在的机房,将消息路由到对应机房的 Broker 所在的分区。

自定义 Partitioner 代码示例(Java):

java
public class VipPartitioner implements Partitioner {
    @Override
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        
        String k = (String) key;
        // 假设 VIP 客户的数据以 "VIP_" 开头,专门放到 0 号分区
        if (k != null && k.startsWith("VIP_")) {
            return 0; 
        }
        
        // 非 VIP 客户走普通的哈希逻辑,分配到 1 到 numPartitions-1 的分区
        return Math.abs(Utils.murmur2(keyBytes)) % (numPartitions - 1) + 1;
    }

    @Override
    public void close() {}

    @Override
    public void configure(Map<String, ?> configs) {}
}

总结与选型建议

业务需求 推荐策略 策略描述
需要保证消息局部有序 (如按订单ID) Default Partitioner 必须指定 Key。Kafka 会按 Key 哈希路由。
无需保序,追求最高吞吐量 Default Partitioner 不要指定 Key,利用 Kafka 2.4+ 的粘性分区。
有 Key,但不想保序,只求高吞吐 UniformStickyPartitioner 强制忽略 Key 的哈希,采用粘性批次发送。
特殊的业务路由规则 (如隔离VIP数据) Custom Partitioner 实现 Partitioner 接口,编写自定义逻辑。
精准控制某条消息去哪个分区 指定 Partition ProducerRecord 中直接写入分区号。
00:00
00:00