综述
在Kafka中,topic是逻辑上的概念,而partition是物理上的概念。不用担心,这些对用户来说是透明的。 生产者(producer)只关心自己将消息发布到哪个topic,而消费者(consumer)只关心自己订阅了哪个topic上的消息,至少topic上的消息分布在哪些partition节点上,它本身并不关心。
设想一下,如果在Kafka中没有分区的话,那么topic的消息集合将集中于某一台服务器上,单节点的存储性能将马上成为瓶颈,当访问该topic存取数据时,吞吐也将成为瓶颈。
介于此,kafka的设计方案是,生产者在生产数据的时候,可以为每条消息人为地指定key,这样消息被发送到broker时,会根据分区规则选择消息将被存储到哪一个分区中。如果分区规则设置合理,那么所有的消息将会被均匀/线性的分布到不同的分区中,这样就实现了负载均衡和水平扩展。
另外,在消费者端,同一个消费组可以多线程并发的从多个分区中同时消费数据。
上述分区规则,实际上是实现了 org.apache.kafka.clients.producer.Partitioner 接口,这个实现类可以根据自己的业务规则进行自定义制定分区,如根据hash算法指定分区的分布规则。
比如在以下的案例类中,我们先获取key的hashcode值,再跟分区数量partitionsNum-1做模运算,结果值作为分区存储位置,这样可以实现数据均匀线性的分布。
下面我们以一个小的案例来介绍Kafka自定义分区器的使用。
1、 创建Maven工程,导入以下依赖:
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>0.9.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.11</artifactId>
<version>0.9.0.0</version>
</dependency>
2、 编写自定义分区器类,实现Partitioner接口
package com.xsluo;
import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.PartitionInfo;
import java.util.List;
import java.util.Map;
/**
* @author:xsluo
* @date:2020/7/10
* @aim:自定义分区器
*/
public class MyPartitioner implements Partitioner {
/**
* 自定义kafka分区主要解决用户分区数据倾斜问题 提高并发效率(假设 3 分区)
* @param topic 消息队列名
* @param key 用户传入key
* @param keyBytes key字节数组
* @param value 用户传入value
* @param valueBytes value字节数组
* @param cluster 当前kafka节点数
* @return 如果3个分区,返回 0 1 2
*/
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自定义分区规则