Kafka
1.windows下安装运行
安装
http://kafka.apache.org/downloads 进入官网下载,下载2.0版本的,3.0版本对windows支持不友好
下载完之后,解压,并创建 两个文件夹 data、Kafka-logs
并进入 config 文件夹,修改配置文件,更改信息输出地址
运行
在根目录下进入命令行
- 先启动zookeeper,
.\bin\windows\zookeeper-server-start.bat .\config\zookeeper.properties
启动成功标志 binging to port
- 启动Kafka
.\bin\windows\kafka-server-start.bat .\config\server.properties
测试
创建topic测试主题kafka
# 创建 create topic 名 chenchong 的主题
.\bin\windows\kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic chenchong
创建消费者,并连接主题
.\bin\windows\kafka-console-producer.bat --broker-list localhost:9092 --topic chenchong
创建消费者,并订阅主题
.\bin\windows\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic chenchong --from-beginning
生产者发布消息到主题 chenchong 订阅了主题的 消费者 就会收到消息。
安装Kafka成功
2.常用命令
=主题操作=
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
因为需要zookeeper支持,所以需要连接zookeeper,以–zookeeper localhost:2181结尾
.\bin\windows\kafka-topics.bat 查看都有那些命令
1.查看都有那些主题 (主题操作)
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
# 命令都以 .\bin\windows\kafka-topics.bat开头
# 中间是具体的命令
--list 查看都有那些主题
# 后边是连接的zookeeper 地址
.\bin\windows\kafka-topics.bat --list --zookeeper localhost:2181
2.创建主题 (主题操作)
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
# 命令都以 .\bin\windows\kafka-topics.bat开头
# 中间是具体的命令
-- create 表明是创建主题
-- replication-factor 1 设置分区副本
-- partitions 1 设置分区数
# 后边是连接的zookeeper 地址
.\bin\windows\kafka-topics.bat --create --replication-factor 1 --partitions 1 --topic test123 --zookeeper localhost:2181
3.查看主题详细描述 (主题操作)
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
# 命令都以 .\bin\windows\kafka-topics.bat开头
# 中间是具体的命令
-- topic 指明要操作那个主题
-- describe 表明该操作是查看详细描述信息
# 后边是连接的zookeeper 地址
.\bin\windows\kafka-topics.bat --topic chenchong --describe --zookeeper localhost:2181
详细描述的参数解释:
Topic : 主题名
PartitionCount:分区数量
ReplicationFactor:副本数量
Configs:底层存储配置
下面是分区一的信息
Topic:所属主题
Partition:当前所属分区序号
Leader: 证明副本 0 是leader副本
Replicas:
Isr:
4.修改主题信息 (主题操作)
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
# 命令都以 .\bin\windows\kafka-topics.bat开头
# 中间是具体的命令
-- topic 指明要操作那个主题
-- alter 表明该操作是修改主题信息
-- partitions 2 表面是修改分区数量为2 (分区数量只能增加,不能减少)
# 后边是连接的zookeeper 地址
.\bin\windows\kafka-topics.bat --topic chenchong --alter --partitions 2 --zookeeper localhost:2181
4.删除主题 (主题操作)
主题操作命令都以 .\bin\windows\kafka-topics.bat开头
# 命令都以 .\bin\windows\kafka-topics.bat开头
# 中间是具体的命令
-- topic 指明要操作那个主题
-- delete 表明该操作是删除主题
# 后边是连接的zookeeper 地址
.\bin\windows\kafka-topics.bat --topic test23 --delete --zookeeper localhost:2181
=生产者操作=
主题操作命令都以 .\bin\windows\kafka-console-producer.bat开头
因为需要连接到kafka,所以需要 --broker-list localhost:9092结尾
kafka端口 9092 可以在配置文件 中修改
.\bin\windows\kafka-console-producer.bat查看都有那些命令
1.创建生产者并与主题产生连接 (生产者操作)
主题操作命令都以.\bin\windows\kafka-console-producer.bat 开头
#命令都以 .\bin\windows\kafka-console-producer.bat开头
# 中间是具体的命令
-- topic 指明要操作那个主题
# 后边是连接的kafka 地址
.\bin\windows\kafka-console-producer.bat --topic chenchong --broker-list localhost:9092
向队列中发布消息
=消费者操作=
主题操作命令都以 .\bin\windows\kafka-console-consumer.bat开头
因为需要连接到kafka,所以需要–bootstrap-server localhost:9092 结尾
.\bin\windows\kafka-console-consumer.bat查看都有那些命令
1.创建消费者,并订阅主题
#命令都以 .\bin\windows\kafka-console-consumer.bat开头
# 中间是具体的命令
-- topic 指明要操作那个主题
-- from-beginning 加上之后,会把主题中历史消息也都读取出来
# 后边是连接的kafka 地址
.\bin\windows\kafka-console-consumer.bat --topic chenchong --from-beginning --bootstrap-server localhost:9092
3.使用
3.1 生产者
=异步发送消息=
定义:一批数据发送之后,不用等处理完就可以处理下一批数据
1.生产者异步发送消息
public class Producer {
public static void main(String[] args) {
//0.配置
Properties properties = new Properties();
//连接kafka
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//指定对应的key value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//1.创建Kafka生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//2.发送数据
for (int i = 0; i < 5; i++) {
//调用send方法发送数据
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,world " + i));
}
//3.关闭资源
kafkaProducer.close();
}
}
消费者可以正常接受消息
2.生产者异步发送消息(带回调信息)
public class Producer {
public static void main(String[] args) {
//0.配置
Properties properties = new Properties();
//连接kafka
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//指定对应的key value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//1.创建Kafka生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//2.发送数据
for (int i = 0; i < 5; i++) {
//调用send方法发送数据,带回调信息
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,world " + i), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e==null){
//没有报错信息,就是正常处理返回
System.out.println("主题:"+recordMetadata.topic()+"分区:"+recordMetadata.partition());
}
}
});
}
//3.关闭资源
kafkaProducer.close();
}
}
打印结果
=同步发送消息=
定义:一批数据发送之后,必须等处理完才可以处理下一批数据
1.生产者同步发送消息
// 只需在正常的异步方法后面加上 .get()方法,并抛出异常即可 同步发送
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,world " + i)).get();
表面上接受效果是一样的,底层会有 同步 异步的区别
=生产者分区=
生产者如果发送消息不指定分区的话,消费者有可能接受不到,因为消费者也没有指定分区
1.消息分区的好处
2.消息分区规则
①.指定分区
返回情况:
②不指定分区,但指定key
会根据Key 的hashCode值来计算。(常用这种,把数据库表名作为key,这样就保证每个表的数据都发到同一个分区)
③ 什么都不指定,粘性分区
为了演示,使用线程睡眠,并多发几条数据
打印结果:
3.自定义分区策略器
自定义分区器之后,kafka原生的就失效了,会按照自定义的进行;
- 1.实现接口,重写方法,创建分区器类
/**
* 自定义 kafka 分区器
* * @author
*/
public class MyPartitioner 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) {
//获取value
String strValue = value.toString();
int partition;
if (strValue.contains("chenchong")) {
partition = 0;
} else {
partition = 1;
}
//返回分区
return partition;
}
@Override
public void close() {
}
@Override
public void configure(Map<String, ?> map) {
}
}
- 2.发送消息时,指定分区策略为使用自定义分区器
- 3.测试
4.提高生产者吞吐量
提高生产者吞吐量,也就是处理生产者产生的消息的速率,有以下几个方面入手:
- 设置批次大小
- 设置等待时间
- 设置缓冲区大小
- 设置压缩
以上配置可能导致消息不及时,根据需要进行配置
/**
* 提高生产者吞吐量
*/
public class ProducerInOut {
public static void main(String[] args) throws InterruptedException {
//0.配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 指定对应的key value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//(以下开始设置提高吞吐量的配置)
//0.3 提高吞吐量-设置批次大小,默认16k,生产环境用 32k
properties.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
//0.4 提高吞吐量-设置等待时间,默认0,这里改大一点,设为1
properties.put(ProducerConfig.LINGER_MS_CONFIG, 1);
//0.5 提高吞吐量-设置缓冲区大小,默认32M
properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
//0.6 提高吞吐量-设置压缩,默认none 可配置值 gzip,snappy,lz4,zstd
properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");
//1.创建Kafka生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//2.发送数据
for (int i = 0; i < 2; i++) {
//调用send方法发送数据
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,world " + i));
}
//3.关闭资源
kafkaProducer.close();
}
}
4.数据可靠
kafka接收到生产者的信息之后,会有一个应答,应答有三种级别:
保证的时数据可靠性,牺牲的是效率
各种级别的风险:
思考:
可靠性总结:
代码演示
/**
* 提高生产者数据可靠
*/
public class ProducerAcks {
public static void main(String[] args) throws InterruptedException {
//0.配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 指定对应的key value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//(以下开始设置 数据可靠 的配置)
//0.3 设置应答级别
properties.put(ProducerConfig.ACKS_CONFIG, "1");
//0.4应答失败重试次数 默认是int 的范围最大值
properties.put(ProducerConfig.RETRIES_CONFIG,3);
//1.创建Kafka生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//2.发送数据
for (int i = 0; i < 2; i++) {
//调用send方法发送数据
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,world " + i));
}
//3.关闭资源
kafkaProducer.close();
}
}
5.数据重复
①幂等性
使用幂等性
②事务
总结:
幂等性,能够保证kafka启动期间数据不重复,但是重启后的数据与之前的有重复的可能性;加上使用事务,能够获取到唯一值的PID,这样重启之后,因为PID是唯一的,因此也不会出现重复数据的情况。
因为幂等性是默认开启的,因此解决数据重复问题,只需要开启事务即可。
代码如下:
/**
* 生产者 消息事务(幂等性+事务)
* 解决数据重复的问题
*/
public class ProducerTranactions {
public static void main(String[] args) throws InterruptedException {
//0.配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 指定对应的key value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//0.3 事务需要指定 事务id (全局唯一)
properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"id"+ UUID.randomUUID().toString());
//1.创建Kafka生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//(事务是针对生产者来说的,因此需要生产者来进行初始化启动)
//1.1初始化事务
kafkaProducer.initTransactions();
//1.2启动事务
kafkaProducer.beginTransaction();
try {
//2.发送数据
for (int i = 0; i < 2; i++) {
//调用send方法发送数据
kafkaProducer.send(new ProducerRecord<>("chenchong", "hello,tranactions " + i));
}
//1.3 提交事务
kafkaProducer.commitTransaction();
} catch (Exception e) {
//发生异常,终止事务,并回滚
kafkaProducer.abortTransaction();
} finally {
//3.关闭资源
kafkaProducer.close();
}
}
}
如果逻辑中报错,则不会收到消息
6.数据有序
3.2 Broker
broker 是kafka 存储数据相关的部分
1.Zookeeper存储
在zookeeper的服务器存储的Kafka相关信息
下面是详细解释:
①/kafka/brokers/ids 记录有哪些服务器
②/kafka/brokers/topics/主题名/partitions/0/state 记录谁是leader,有哪些服务器可用
③/kafka/controller 辅助选取leader
3.3 消费者
1.kafka消费者消费方式
2.工作流程
消费者分为 单独消费者 与 消费者组
单独消费者,可以选择消费单独的一个分区的数据,也可以选择消费多个分区的数据。
消费者组,是由多个消费者组成的组,组内每个消费者选择的消费分区不允许重复。
①消费者组工作流程
每个单独的消费者其实也是一个组,但是由于没有其他的消费者与其争资源,因此它可以消费所有分区。
如果组内消费者超过分区数,那么会有一部分消费者闲置;
组内消费者消费分区不允许重复,但是组与组之间不受影响
消费者组 初始化
1.消费者组会利用自己的组id,来根据策略计算出选用那个分区的协调器来作为基准;选出基准后,每个消费者都向该协调器发请求,竞争出来消费者leader,由该消费者leader来进行每个消费者的分区选择。
消费者组 消费
消费过程原理:
接下来,进行代码实操
3.消费者API
①订阅主题,监听消息
/**
* 消费者
*/
public class Consumer {
public static void main(String[] args) {
//0 配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 因为生产者进行了序列化,所以消费者需要定义反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//0.3 配置消费者组id
properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test");
//1.创建消费者
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
//2.订阅想要消费的主题
List<String> topics = new ArrayList<>();
topics.add("chenchong");
kafkaConsumer.subscribe(topics);
//3.消费数据 (业务中会选取一个逻辑进行终止拉取的判断,这里简单的设为一直拉取)
while (true) {
//poll 参数为 每批次拉取之间的间隔时间,这里设为 每一秒拉取一次
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
//打印
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord);
}
}
}
}
利用生产者向主题中发送消息:
控制台打印:
ConsumerRecord(
topic = chenchong, partition = 0, leaderEpoch = 0, offset = 36,
CreateTime = 1659360500809, serialized key size = -1, serialized value size = 6,
headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = nihaoa
)
②订阅主题,并指定分区拉取
/**
* 消费者,指定分区消费
*/
public class ConsumerPattition {
public static void main(String[] args) {
//0 配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 因为生产者进行了序列化,所以消费者需要定义反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//0.3 配置消费者组id
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
//1.创建消费者
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
//2.订阅想要消费的主题,以及主题的指定分区
List<TopicPartition> topicPartitions = new ArrayList<>();
topicPartitions.add(new TopicPartition("chenchong",1));
kafkaConsumer.assign(topicPartitions);
//3.消费数据 (业务中会选取一个逻辑进行终止拉取的判断,这里简单的设为一直拉取)
while (true) {
//poll 参数为 每批次拉取之间的间隔时间,这里设为 每一秒拉取一次
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
//打印
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord);
}
}
}
}
利用生产者向指定分区发送数据:
控制台打印:
不向订阅指定的分区发送的话,消费者就拉取不到
4.分区分配策略
① Range + CooperativeSticky 分配 (默认)
② RoundRobin 分配
代码演示:
同一组内每个消费者的分区策略应当保持一致,这样不管那个消费者为主导,都会是相同的分区策略;
③ Sticky 粘性分配
同Range分配一样,先取模,多余的往前几个分配,不同的是,Range是分区有序的,Sticky是分区无序的。
代码只需要改为 粘性策略 即可:
5.offset
默认存放位置
kafka有一个默认系统主题 __consumer_offsets 用来存放offset
①offset 自动提交
代码:
//0.4 设置自动提交,及自动提交间隙 (默认就是 true 这里不写也可以)
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
//设置自动提交频率为一秒一次
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
②offset 手动提交
因为自动提交,导致无法准确的把控提交时机,因此,kafka还提供手动提交API
代码如下:
/**
* 消费者 offset 手动提交
*/
public class ConsumerOffsetByHand {
public static void main(String[] args) {
//0 配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 因为生产者进行了序列化,所以消费者需要定义反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//0.3 配置消费者组id
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
//0.4 关闭自动提交
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
//1.创建消费者
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
//2.订阅想要消费的主题
List<String> topics = new ArrayList<>();
topics.add("chenchong");
kafkaConsumer.subscribe(topics);
//3.消费数据 (业务中会选取一个逻辑进行终止拉取的判断,这里简单的设为一直拉取)
while (true) {
//poll 参数为 每批次拉取之间的间隔时间,这里设为 每一秒拉取一次
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
//打印
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord);
}
//拉取完后,手动提交offset
kafkaConsumer.commitSync();//同步提交
kafkaConsumer.commitAsync();//异步提交
}
}
}
6.消费方式
①指定offset消费
上面三种策略,都没有指定位置,下面来进行指定位置
代码如下:
/**
* 消费者 指定offset消费
*/
public class ConsumerSeek {
public static void main(String[] args) {
//0 配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 因为生产者进行了序列化,所以消费者需要定义反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//0.3 配置消费者组id
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
//1.创建消费者
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
//2.订阅想要消费的主题
List<String> topics = new ArrayList<>();
topics.add("chenchong");
kafkaConsumer.subscribe(topics);
//3 指定位置进行消费
//3.1先获取全部分区,
Set<TopicPartition> assignment = kafkaConsumer.assignment();
//3.2因为有可能分区还没有分配完成,因此需要先保证分区分配完成
while (assignment.size() == 0) {
//主动拉取消息,加快分区完成
kafkaConsumer.poll(Duration.ofSeconds(1));
//再次获取分区,更新值
assignment = kafkaConsumer.assignment();
}
//3.2 获取到分区信息,才可以进行遍历分区
for (TopicPartition topicPartition : assignment) {
//遍历分区,指定offset位置为100 (遍历的意义是保证无论消费那个分区,都是从这个offset位置开始拉取)
kafkaConsumer.seek(topicPartition, 100);
}
//3.消费数据 (业务中会选取一个逻辑进行终止拉取的判断,这里简单的设为一直拉取)
while (true) {
//poll 参数为 每批次拉取之间的间隔时间,这里设为 每一秒拉取一次
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
//打印
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord);
}
}
}
}
②指定时间消费
/**
* 消费者 指定时间进行消费
*/
public class ConsumerSeekTime {
public static void main(String[] args) {
//0 配置
Properties properties = new Properties();
//0.1 连接kafka
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
//0.2 因为生产者进行了序列化,所以消费者需要定义反序列化
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
//0.3 配置消费者组id
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
//1.创建消费者
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
//2.订阅想要消费的主题
List<String> topics = new ArrayList<>();
topics.add("chenchong");
kafkaConsumer.subscribe(topics);
//3 指定时间进行消费
//3.1先获取全部分区,
Set<TopicPartition> assignment = kafkaConsumer.assignment();
//3.2因为有可能分区还没有分配完成,因此需要先保证分区方案分配完成
while (assignment.size() == 0) {
//主动拉取消息,加快分区完成
kafkaConsumer.poll(Duration.ofSeconds(1));
//再次获取分区,更新值
assignment = kafkaConsumer.assignment();
}
//3.3 因为没有直接指定时间的api,因此需要把时间换算成对应的offset
Map<TopicPartition, Long> topicPartitionLongMap = new HashMap<>();
for (TopicPartition topicPartition : assignment) {
//放入 分区-指定时间的时间戳
topicPartitionLongMap.put(topicPartition, System.currentTimeMillis() - 1 * 24 * 3600 * 1000);
}
Map<TopicPartition, OffsetAndTimestamp> topicPartitionOffsetAndTimestampMap = kafkaConsumer.offsetsForTimes(topicPartitionLongMap);
//3.2 根据 分区-时间戳转化的offset Map,进行消费
for (TopicPartition topicPartition : assignment) {
OffsetAndTimestamp offsetAndTimestamp = topicPartitionOffsetAndTimestampMap.get(topicPartition);
kafkaConsumer.seek(topicPartition, offsetAndTimestamp.offset());
}
//4.消费数据 (业务中会选取一个逻辑进行终止拉取的判断,这里简单的设为一直拉取)
while (true) {
//poll 参数为 每批次拉取之间的间隔时间,这里设为 每一秒拉取一次
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
//打印
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord);
}
}
}
}
③漏消费与重复消费分析
如果想要规避掉上面两种情况,我们引入事务
④ 事务
⑤ 数据积压
完结
参考视频:尚硅谷kafka