[kafka扫盲]---(6)kafka源码阅读之分区器

版权声明: https://blog.csdn.net/zhaoyaxuan001/article/details/83241717
Author:赵志乾
Date:2018-10-21
Declaration:All Right Reserved!!!

DefaultPartitioner.java

该类实现了Partitioner接口,核心方法为partition():用于给未指定分区号的消息记录生成分区号,其生成策略也比较简单。其依据是否指定key值采用两种不同的策略:如果指定key值,则按照key的hash来生成分区号,如果未指定key值,则按照轮询策略来生成分区号。

为了实现轮询策略,该类内部维护了一个实例字段:topicCounterMap,其类型为ConcurrentMap。该字段用于维护topic名称到topic在当前客户端中所持有的计数器。

如果要实现自定义的分区器,可以通过实现Partitioner接口来完成。

package org.apache.kafka.clients.producer.internals;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.utils.Utils;

/*该类的实例是由kafka客户端默认的分区器,提供默认的分区策略:如果生产者要发布的消息记录指定了分区
号,则直接使用该分区号进行二级分属划分;如果没有指定分区号,而是指定了key值,则使用该key的hash值
来生成分区号,进行消息记录的二级分属划分;如果分区号和key都没有指定,则通过轮询的方式生成一个分区
号,进行二级分属划分*/
public class DefaultPartitioner implements Partitioner {
    //主题计数器,用于轮询策略使用
    private final ConcurrentMap<String, AtomicInteger> topicCounterMap = new ConcurrentHashMap<>();

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

    /*为指定消息记录计算分区号,所需参数包括:主题、key、key序列化后的字节数组、value、value序
列化后的字节数组、kafka集群元数据*/
    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();
        //如果没有指定key值,便采用轮询的方式生成分区号
        if (keyBytes == null) {
            //获取主题下一个计数值
            int nextValue = nextValue(topic);
            //获取集群上指定主题下的可用分区信息
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic
(topic);    
            //如果存在可用分区
            if (availablePartitions.size() > 0) {
                //按轮询策略得到分区号
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                //没有可用分区时,按主题下所有分区参与轮询计算,返回一个不可用分区号            
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            如果存在key值,则通过对key值hash的方式返回一个分区号            
            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();
    }

    public void close() {}
}

猜你喜欢

转载自blog.csdn.net/zhaoyaxuan001/article/details/83241717