目录
基于kafka新版本
<dependencies>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
一、kafkaProducer
1、介绍
设计上比consumer要简单一些,因为不涉及组管理,即每个producer都是独立工作的。
(1)目前producer的主要功能是向某个topic的某个分区发送一条消息,这就涉及分区选择策略,在ProducerRecord中介绍。
(2)因为有ISR,因此在发送消息时,producer有多种选择来实现消息发送,如不等待任何副本的影响便返回、只等待leader副本响应返回等等。
2、发送流程
produce的发送主要流程概述如下:
-
拦截器对发送的消息拦截处理;
-
获取元数据信息;
-
序列化处理;
-
分区处理;
-
批次添加处理;
-
发送消息。
3、主要参数
3.1、acks:
有三个参数:0、1、all,数据可靠性的重要参数,可以保证消息不丢失。
Properties properties = new Properties();
properties.put(ProducerConfig.ACKS_CONFIG,"1");
3.2、 buffer.memory
指定了producer端用于缓冲消息的缓冲区大小,单位是字节。
3.3、compression.type
producer是否压缩消息。
3.4、batch.size
二、ProducerRecord
1、介绍
发送给Kafka Broker的key/value 值对,producer将待发送的消息封装进ProducerRecord实例类。
不设置分区时,默认只有一个分区;如果需要设置分区数量,需要在创建topic时指定。
2、发送消息分区策略
(1)指定了分区:
partition不为空。
当发送时指定了partition就使用该partition。即kafka生产者发送的消息ProducerRecord(String topic, Integer partition, K key, V value)指定了发送到哪个具体的分区。
(2)轮询
partition=null && key=null。
既没有partition值又没有key值的情况下,第一次调用是随机生成一个整数(后面调用在这个整数上自增),将这个值与topic可用的parition总数取余得到partition值,也就是常说的round-robin算法。
如果kafka生产者发送的消息ProducerRecord(String topic, Integer partition, K key, V value)没有指定发送到哪个具体的分区,即partition=null(并且key也为空时,如果此时key不为空的话就会采用另一种分区策略key哈希分区策略),并且使用了默认的分区器,那么消息将被随机的发送到主题的各个可用分区上,分区器使用轮询的算法将消息均衡的分布到各个分区。
(3)key哈希分区策略
partition=null && key != null。
没有指明partition但有key,将key的hash值与topic的partition数据进行取余得到partition值。
根据消息的key进行哈希计算,并将消息发送到对应的分区。保证相同key的消息始终被发送到同一个分区,确保消息的顺序性。
(4)自定义分区策略(即自定义Partitioner)
用户可以根据自己的需求实现自定义的分区策略,通过实现org.apache.kafka.clients.producer.Partitioner接口来自定义分区选择逻辑。
三、 KafkaConsumer
1、使用
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;
public class CustomConsumer {
public static void main(String[] args) {
// 1. 创建消费者配置对象
Properties properties = new Properties();
// 2. 给消费者配置对象添加参数(不同于生产者,消费者有 4个必要的配置参数)
// broker的ip地址
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop102:9092");
// 配置 反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//配置消费者组(组名必须)
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "group1");
// 3. 创建消费者对象
KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);
// 注册消费主题
ArrayList<String> topics = new ArrayList<>();
topics.add("first");
consumer.subscribe(topics);
// 4.调用方法消费数据
// 如果kafka集群没有新数据会造成空转
// 填写参数为时间,如果没有拉取数据,线程睡眠一会
while (true) {
// 设置1s中消费的一批数据
// Duration.ofSeconds(1)不会导致空转,拉取不到的时候睡眠1s
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
// 打印消费数据
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.topic() + "-" + consumerRecord.partition() + "-" + consumerRecord.offset());
}
}
//5.关闭资源
// consumer.close();不使用的原因是,已关闭进程,就不会再消费数据了,进程停止就以为着JVM为断电了,不再工作
}
}
2、消费者分配策略
消费者的基本规则:1)一个分区的数据只能由这个消费者组中的某一个消费者消费;
2)一个消费者可以消费多个分区的数据。
决定多个分区如何分配给多个消费者,即我们所说的消费者分配策略,它由 partition.assignment.strategy 属性的值来决定
这里要与 Kafka 生产者的分区规则区分开,
-
生产者的分区规则指的是:
生产者在生产数据过程中,一个Topic有多个分区,数据写入哪个分区。 -
而 Kafka 消费者分配策略指的是:多个Topic 多个分区,一个消费者组有多个消费者(即多个相同的groupId),Kafka 是怎么样自动分配保证消费的负载均衡的。
Kafka 消费者的分配策略有三种:范围分配、轮询分配、黏性分配。Kafka 默认的分配策略是范围分配。在Kafka 2.0版本之前,一般用轮询分配,但是Kafka 2.0 版本之后,建议使用黏性分配。因为黏性分配不仅考虑了分配的均衡度之外,在重分配的情况代价更低,效率更高。
2.1、RangeAssignor 范围分配
默认的分配策略。
针对每个消费者的每个Topic 进行范围分配。即每个消费者消费一定范围的分区,尽量地实现将分区均分给不同的消费者,如果不能均分,优先将分区分配给消费者编号比较小的消费者。并且每个消费者消费的范围是连续的;不能均分的情况下:消费者由Kafka 自动编号。Kafka 会优行把多出来的分区分给消费者组中编号比较小的消费者。
如只有1个Topic,共6个分区。有4个消费者共同订阅了这个Topic。如果平均每人消费1个分区,则最后多出来2个分区 Kafka 会默认分给编号较小的消费者。并且每个消费者消费的范围是连续的:
consumer1: t0-0 t0-1
consumer2: t0-2 t1-0
consumer3: t1-1
consumer4: t1-2
优点:如果Topic 的个数比较少,分配会相对均衡(只有一个Topic的情况下,消费者消费分区的个数最多只差1个);
缺点:因为范围分配是针对每个Topic对消费者进行范围分配。当存在多个Topic,并且分区个数都不能均分的情况,会导致负载不均衡的问题(即编号比较小的消费者负担会更重)。
2.2、RoundRobinAssignor 轮询分配
Kafka 2.0 版本之前使用。按字面意思,Kafka 会根据 Topic 订阅来轮询分配,将所有的分区 按 Topic名称和分区编号进行排序,轮询分配给每个消费者(如果第一轮轮询结束,会接着上一轮轮询分配的结果继续轮询分配)。
优点:如果有多个消费者,消费的Topic 都是一样的,实现将所有的Topic 的所有分区轮询分配给所有消费者,尽量地实现负载均衡。
缺点:
(1)遇到极端情况下,消费者订阅的 Topic 是不一致的,不同的消费者订阅了不同 Topic ,Kafka 只基于订阅的消费者进行轮询分配,导致整体消费负载不均衡。
假设有3个消费者消费3个Topic。
Topic1 有1 个分区;
Topic2 有2个分区;
Topic3 有3个分区;Topic1 数据比较多,所以消费者c1、c2、c3都订阅了 Topic1;
Topic2 数据较少些,所以消费者c2、c3 订阅了 Topic2;
Topic3 数据最少,所以只有消费者 c3 订阅了 Topic3;此时,根据轮询规则,则
Topic : 对应的消费者
T1 - 0 :c1
T2 - 0 :c2
T2 - 1 :c3
T3 - 0 :c3
T3 - 1 :c3
T3 - 2 :c3
(2)负载不均衡,如果遇到某个消费者故障,范围分配和轮询分配策略都会将所有的分区重新分配【性能较差】
应用:所有消费者都订阅共同的 Topic,能实现让所有Topic 的分区轮询分配给所有的消费者
2.3、StickyAssignor 黏性分配
这是 Kafka 2.0 版本之后建议使用。
优点:黏性分配除了考虑分配的均衡度以外,重分配的效率更高。
规则:类似于轮询分配,尽量地将分区均衡地分配给消费者;
这种策略下,消费者会尽量保持与之前分配的分区相同。如果有新的消费者加入或有消费者退出,分区的重新分配会尽量减少。这个策略对于需要保持状态的应用程序比较有用。
特点:
(1)相对保证分配的均衡【比轮询还要均衡】;
(2)如果某个消费者故障,尽量地避免网络传输,不会重新分配
· 尽量保证原来的消费的分区不变,将多出来分区均衡给剩余的消费者;
假设原来有3个消费者,消费6个分区,平均每个消费者消费2个分区;如果有一个消费者故障了,这个消费者负责的分区交给剩下的消费者来做:消费重平衡
2.4、CooperativeSticky(合作者粘性)
后面版本加的分配策略:
这是Kafka 2.4.0版本引入的新策略,通过考虑消费者的健康状况、处理速度、网络延迟等因素,动态地进行分区分配,以实现更好的负载均衡和消费者协作。