版权声明: 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() {}
}