【kafka专栏】生产者客户端自定义分区器实现向指定分区发送消息

kafka生产者的数据生产流程中,有三个环节是我们可以自定义的,如下图所示。本文为大家介绍如何自定义kafka生产者分区器。
在这里插入图片描述

一、生产者默认分区策略

分区策略作用就是指消息根据什么规则,被发往主题的哪个分区。

  • 默认策略一:如果生产者指定了partition分区,就直接发送到该partition分区
  • 默认策略二:如果没有指定分区但是指定了key,就按照key的hash值选择分区,具有相同key值的消息将被发往同一个分区。
  • 默认策略三:如果partition和key都没有指定就使用轮询策略,能保证消息相对均衡的分配给同一个主题下的多个分区。

二、消息有序性的保障

一再说明的是“消息是按照主题分区进行发送的”,所以如果希望保证消息数据发送的有序性,以及消费者消费数据的有序性,就必须将这些消息发往同一个分区。
怎么发往同一个分区?三种方法:

  • 生产者指定partition,不建议这种,因为人为指定会造成数据在不同分区之间分配不均。有的分区压力大,有的分区没压力。
  • 需要发往同一个分区的消息,指定相同的key,具有相同的hash值,传递到同一个分区。Kafka根据传递消息的key来进行分区的分配,即hash(key) % numPartitions,下文是默认分区源码:
def partition(key: Any, numPartitions: Int): Int = {
    
    
    Utils.abs(key.hashCode) % numPartitions
}
  • 如果通过key的方法,无法满足你的分区需求,我们就可以通过自定义分区器的方式来实现分区逻辑。自定义分区器的优先级最高,将覆盖其他的分区规则

更多的关于kafka生产者保证消息的有序性及其相关原理配置,已经在本专栏的《保证消息顺序性》那一篇文章中介绍,本文不做更多赘述,本文主要为大家介绍如何自定义消息分区器。

三、分区器接口

生产者分区器接口如下,如果希望自定义分区发送逻辑,就需要实现这个接口

package org.apache.kafka.clients.producer;

import org.apache.kafka.common.Configurable;
import org.apache.kafka.common.Cluster;

import java.io.Closeable;

/**
 * 分区器接口
 */
public interface Partitioner extends Configurable, Closeable {
    
    

    /**
     * 根据消息record信息对其进行重新分区
     *
     * @param topic 主题名称
     * @param key 用于分区的key对象
     * @param keyBytes 用于分区的key的二进制数组
     * @param value 生产者消息对象
     * @param valueBytes 生产者消息对象的二进制数组
     * @param cluster 当前kafka集群的metadata信息
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);

    /**
     * 当分区器执行完成时被调用
     */
    public void close();


    default public void onNewBatch(String topic, Cluster cluster, int prevPartition) {
    
    }
}

四、自定义分区器

下面代码实现生产者自定义分区器,实现Partitioner接口。我自定义的逻辑比较简单,如果key=zimug,将数据发往主题中的最后一个分区,否则发往第一个分区。假如某个主题有5个分区,分区编号0-4,那么 如果生产者发送消息数据的key=zimug,数据发往4号分区,否则发往0号分区。

public class MyProducerPartitioner implements Partitioner {
    
    

    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
    
    
        //获取topic的partitions信息
        List<PartitionInfo> partitionInfos = cluster.partitionsForTopic(topic);
        int partitionsNum = partitionInfos.size();
        //为特定的key自定义分区规则
        if (key != null && key.toString().equals("zimug")) {
    
    
            //分配到最后一个分区
            return partitionsNum - 1;
        }else{
    
    
            //分配到0号分区,即主题中的第一个分区
            return 0;
        }
    }
    
    public void close() {
    
    }
    
    public void configure(Map<String, ?> map) {
    
    }
}

上文这个自定义分区器只是为了讲解自定义生产者分区器,真实生产中不要将数据集中发往主题中的某2个分区,造成数据分配不均。这里只是简单的介绍,不包含实际的应用业务。

五、指定生产者分区器

为生产者指定自定义分区器,这样配置完成之后,生产者再次发送消息时,会遵守MyProducerPartitioner的partition方法中定义的分区规则,将数据发往指定的分区。

properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG,MyProducerPartitioner.class.getName())

猜你喜欢

转载自blog.csdn.net/hanxiaotongtong/article/details/125576297