【生产者篇】 初始化分区器和DefaultPartitioner分区实现分析

一、分区器的初始化时机
1、在KafkaProducer获取分区器,
              /**
             * 读取使用的分区器
             * 分区器配置名properties.put("partitioner.class" ,"全类名")
             * 如果配置了将会使用用户自定义的分区器,如果用户没有自定义分区器,这里将会使用
             * class org.apache.kafka.clients.producer.internals.DefaultPartitioner
             * 分区器进行分区。
             * 具体kafka是如何实现这个功能的点进去源码分析
             * config.getConfiguredInstance(),就是读取配置中的一个,接口实例
             */
  this.partitioner = config.getConfiguredInstance(ProducerConfig.PARTITIONER_CLASS_CONFIG, Partitioner.class);
2、 调用根据配置获取实例对象方法
    /**
     * 获取T接口的的配置实例对象
     *
     * @param key   配置ksy
     * @param t     接口
     * @return A    配置的value实例
     */
    public <T> T getConfiguredInstance(String key, Class<T> t) {
       //获取配置的Class对象
        Class<?> c = getClass(key);
        if (c == null)
            return null;
        //反射创建实例
        Object o = Utils.newInstance(c);
        //类型判断
        if (!t.isInstance(o))
            throw new KafkaException(c.getName() + " is not an instance of " + t.getName());
        if (o instanceof Configurable)
            //配置到用户自定义配置
            ((Configurable) o).configure(originals());
        return t.cast(o);
    }

现在看看如何根据

3、从解析配置map中获取配置值

Class<?> c = getClass(“partitioner.class”);获取到的用户护着默认的分区器实例

protected Object get(String key) {
    if (!values.containsKey(key))
        throw new ConfigException(String.format("Unknown configuration '%s'", key));
    //添加到使用配置
    used.add(key);
    //从已经解析配置中获配置值
    return values.get(key);
}
解析配置中读取的配置    private final Map<String, Object> values;

在ProducerConfig中的初始化默认配置源码中可以看出:

配置的是 DefaultPartitioner.class

.define(PARTITIONER_CLASS_CONFIG,
        Type.CLASS,
        DefaultPartitioner.class,
        Importance.MEDIUM, PARTITIONER_CLASS_DOC)

如果用户有自己的配置partitioner.class值,就会在

ProducerConfig的父类AbstractConfig的构造方法
 AbstractConfig(ConfigDef definition, Map<?, ?> originals, boolean doLog)
 中将用户的自定义值覆盖掉默认的值

在实例化KafkaProducer时候,初始化分区器是读取partitioner.class 配置,该配置默认使用DefaultPartitioner.class,然后使用反射创建对象,然后赋值给KafkaProducer的partitioner成员中。

二、分区器的体系
1、体系图

分区器接口:Partitioner,Kafka默认采用其唯一实现类DefaultPartitioner

在这里插入图片描述

2、DefaultPartitioner分区实现:

当指定了key,则采用固定的算法,每次都会计算出同一分区号,如果key不存在则计算出一个可用分区的的一个任意分区

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param key The key to partition on (or null if no key)
     * @param keyBytes serialized key to partition on (or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        // 获取该topic所有分区 【见2.1】
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        // key不存在
        if (keyBytes == null) {
            // 获取该topic计数器
            int nextValue = nextValue(topic);
            // 获取可用分区
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
            if (availablePartitions.size() > 0) {
                // 计算一个分区,key不存在的到是可用分区的随机一个
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                // no partitions are available, give a non-available partition
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            // key存在,key存在的情况下,每次得到的都是统一分区
            // hash the keyBytes to choose a partition
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }

    private int nextValue(String topic) {
        // 计数器
        AtomicInteger counter = topicCounterMap.get(topic);
        if (null == counter) {
            // 随机产生一个随机数
            counter = new AtomicInteger(ThreadLocalRandom.current().nextInt());
            AtomicInteger currentCounter = topicCounterMap.putIfAbsent(topic, counter);
            if (currentCounter != null) {
                counter = currentCounter;
            }
        }
        return counter.getAndIncrement();
    }

2.1获取指定topic的所有分区

Cluster是Kafka集群的模型,其中维护了

/**
 * topic 与 partition 的对应关系
 * 一个topic 对应多个partition
 */
private final Map<String, List<PartitionInfo>> partitionsByTopic;
private final Map<String, List<PartitionInfo>> availablePartitionsByTopic;
public List<PartitionInfo> partitionsForTopic(String topic) {
    //
    List<PartitionInfo> parts = this.partitionsByTopic.get(topic);
    return (parts == null) ? Collections.<PartitionInfo>emptyList() : parts;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值