分区好处
1)便于合理使用存储资源,每个Partition在一个Broker上存储,可以把海量的数据按照分区切割成一块一块数据存储在多台Broker上。合理控制分区的任务,可以实现负载均衡的效果。
2)提高并行度,生产者可以以分区为单位发送数据;消费者可以以分区为单位进行消费数据。
生产者发送消息的分区策略
由上图可知,可以自行在ProducerRecord类中去测试
自定义分区器
需求:例如我们实现一个分区器实现,发送过来的数据中如果包含 huanhuan,就发往 0 号分区,包含 hao,就发往 1 号分区。不包含上述两个数据,就发往2号分区
实现步骤:
1)新建Topic为second ,并设置3个分区
bin/kafka-topics.sh --bootstrap-server hadoop102:9092 --create --partitions 3 --replication-factor 3 --topic second
2)在虚拟机上开启消费者
[root@hadoop103 kafka]# bin/kafka-console-consumer.sh --bootstrap-server hadoop102:9092 --from-beginning --topic second
新建自定义分区器类MyPartition
package com.apache.producer;
import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;
import java.util.Map;
public class MyPartition implements Partitioner {
/**
* 返回信息对应的分区
*
* @param topic 主题
* @param key 消息的 key
* @param keyBytes 消息的 key 序列化后的字节数组
* @param value 消息的 value
* @param valueBytes 消息的 value 序列化后的字节数组
* @param cluster 集群元数据可以查看分区信息
* @return
*/
@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
//获取数据,huanhuan hao 以及其他数据
String message = value.toString();
// 创建 partition
int partition;
//判断数据中是否包含 huanhuan 和 hao
if (message.contains("huanhuan")) {
partition = 0;
} else if (message.contains("hao")) {
partition = 1;
} else {
partition = 2;
}
//返回分区号
return partition;
}
//关闭资源
@Override
public void close() {}
//配置方法
@Override
public void configure(Map<String, ?> configs) {}
}
新建测试类MyPartitioner
package com.apache.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class MyPartitioner {
public static void main(String[] args) {
//配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092,hadoop103:9092");
//指定对应key value序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//指定自定义分区器
properties.put(ProducerConfig.PARTITIONER_CLASS_CONFIG, "com.apache.producer.MyPartition");
//创建kafka生产者
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//发送数据
for (int i = 0; i <= 5; i++) {
//添加回调函数
kafkaProducer.send(new ProducerRecord<>("second", "huanhuan" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception exception) {
//查看exception是否为空,如果为空,则打印成功并返回信息,如果失败则不打印,依旧返回信息
if (exception == null) {
System.out.println("主题:" + recordMetadata.topic() + "\t 分区:" + recordMetadata.partition());
}
}
});
//添加回调函数
kafkaProducer.send(new ProducerRecord<>("second", "hao" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception exception) {
//查看exception是否为空,如果为空,则打印成功并返回信息,如果失败则不打印,依旧返回信息
if (exception == null) {
System.out.println("主题:" + recordMetadata.topic() + "\t 分区:" + recordMetadata.partition());
}
}
});
//添加回调函数
kafkaProducer.send(new ProducerRecord<>("second", "Bigdata" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception exception) {
//查看exception是否为空,如果为空,则打印成功并返回信息,如果失败则不打印,依旧返回信息
if (exception == null) {
System.out.println("主题:" + recordMetadata.topic() + "\t 分区:" + recordMetadata.partition());
}
}
});
}
//关闭资源
kafkaProducer.close();
}
}
idea控制台结果如下:
虚拟机终端结果如下:
所对应控制台三个分区!