消费者API _Consumer
Consumer消费数据时的可靠性是很容易保证的,因为数据在Kafka中是持久化的,故不用担心数据丢失问题。
由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。所以offset的维护是Consumer消费数据是必须考虑的问题。
1.自动提交offset
(1)编写代码:需要用到的类:
KafkaConsumer:需要创建一个消费对象,用来消费
ConsumerConfig:需要所需的配置参数
ConsumerRecord: 每条数据都要封装成一个ConsumerRecord对象
为为了使我们能够专注于自己的业务逻辑,Kafka提供了自动提交offset的功能。
自动提交offset的相关参数:
enable.auto.commit:是否开启自动提交offset功能
auto.commit.interval.ms:自动提交offset的时间间隔
java代码:
package com.itwise.kafka.consumer;
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.clients.producer.KafkaProducer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;
public class MyConsumerDome {
/**
* 演示消费者
*/
public static void main(String[] args) {
// 创建连接数
Properties prop = new Properties();
// 连接用户
prop.put("bootstrap.servers", "node2:9092");
// 创建组号,看是否为新
prop.put("group.id", "java");
// 是否自动提交
prop.put("enable.auto.commit", "true");
// 设置提交时间,1000要加引号!!!!
prop.put("auto.commit.interval.ms", "1000");
// 连接
prop.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
prop.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
// offset重设
// 最早的,开头,就是from-beginning
prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
// 最新的,也就是末尾
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"latest");
// 默认
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"none");
// 与kafka进行连接,将上面设置的参数添加进取
// 注意是消费者!!!!!
KafkaConsumer<String, String> kafkaConsumer
= new KafkaConsumer<String, String>(prop);
// KafkaProducer<String, String> stringStringKafkaProducer = new KafkaProducer<String, String>(prop);
// 消费者订阅消息,定义集合储存主题
ArrayList<String> topicList = new ArrayList<>();
topicList.add("itwise"); //添加主题
//topiclist.add("hadoop11"); //没有topic,会创建topic,1分区1副本的topic
// 添加消费者消费的 主题
kafkaConsumer.subscribe(topicList);
// 消费者循环取出数据
while (true){
ConsumerRecords<String, String> poll = kafkaConsumer.poll(Duration.ofSeconds(2));
// 强循环取出数据输出到控制台
for (ConsumerRecord<String, String> ConsumerRecord : poll) {
System.out.println(ConsumerRecord.topic()+"--"+
ConsumerRecord.partition()+"--"+
ConsumerRecord.offset()+"--"+
ConsumerRecord.key()+"--"+
ConsumerRecord.value());
}
}
// 一直等待接收,不需要关闭
// kafkaConsumer.close();
}
}
虚拟机建立生产者:producer 输入数据
[itwise@node2 ~]$ kafka-console-producer.sh --broker-list node2:9092 --topic itwise
>aaaa
1,最早的结果展示: 有旧原有的数据
2.最末尾的数据: 不会产生原有的数据
3 none:
2重置offset
auto.offset.rest = earliest | latest | none |
注意:
知识点: 一组消费者,每个消费者负责一个分区,itwise会自动的去记录维护offset,但是对于一个后开启的新组的消费者,则看不到该生产者生产的topic中以前的数据, 因为 offset默认的是latest,位置
在最后。需要观察的是:新组(组号要是新的),修改消费者代码中设置auto.offset.reset=earliest,然后启动消费者代码。观察是否将分区中以前的数据读取出来了。
手动提交offset
虽然自动提交offset十分简介便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因此Kafka还提供了手动提交offset的API。手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。两者的相同点是,都会将本次poll的一批数据最高的偏移量提交;不同点是,commitSync阻塞当前线程,
一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而commitAsync则没有失败重试机制,故有可能提交失败。
(1)同步提交offset
创建一个topic
kafka-topics.sh --create --topic my_Consumer --bootstrap-server node2:9092 --
partitions 2 --replication-factor 3
# 生产数据:
kafka-console-producer.sh --broker-list node2:9092 --topic my_Consumer
# 消费者
kafka-console-consumer.sh --bootstrap-server node2:9092 --topic my_Consumer
(2)java同步代码实现:
package com.itwise.kafka.consumer;
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 java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;
public class MyConsumerDome_tonbu {
/**
* 演示消费者
*/
public static void main(String[] args) {
// 创建连接数
Properties prop = new Properties();
// 连接用户
prop.put("bootstrap.servers", "node2:9092");
// 创建组号,看是否为新
prop.put("group.id", "ConsumerDome");
// 关闭自动提交
prop.put("enable.auto.commit", "false");
// 设置提交时间,1000要加引号!!!!
// prop.put("auto.commit.interval.ms", "1000");
// 连接
prop.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
prop.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
// offset重设
// 最早的,开头
prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
// 最新的,也就是末尾
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"latest");
// 默认
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"none");
// 与kafka进行连接,将上面设置的参数添加进取
// 注意是消费者!!!!!
KafkaConsumer<String, String> kafkaConsumer
= new KafkaConsumer<String, String>(prop);
// KafkaProducer<String, String> stringStringKafkaProducer = new KafkaProducer<String, String>(prop);
// 消费者订阅消息,定义集合储存主题
ArrayList<String> topicList = new ArrayList<>();
topicList.add("my_Consumer"); //添加主题
//topiclist.add("my_Consumer"); //没有topic,会创建topic,1分区1副本的topic
// 添加消费者消费的 主题
kafkaConsumer.subscribe(topicList);
// 消费者循环取出数据
while (true){
ConsumerRecords<String, String> poll = kafkaConsumer.poll(Duration.ofSeconds(2));
// 强循环取出数据输出到控制台
for (ConsumerRecord<String, String> ConsumerRecord : poll) {
System.out.println(ConsumerRecord.topic()+"--"+
ConsumerRecord.partition()+"--"+
ConsumerRecord.offset()+"--"+
ConsumerRecord.key()+"--"+
ConsumerRecord.value());
}
System.out.println("同步提交offset");
kafkaConsumer.commitAsync();
}
// 一直等待接收,不需要关闭
// kafkaConsumer.close();
}
}
同步: 拉取信息与提交文字同步,再提取下一个
异步代码的实现
package com.itwise.kafka.consumer;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
public class MyConsumerDome_yibu {
/**
* 演示消费者
*/
public static void main(String[] args) {
// 创建连接数
Properties prop = new Properties();
// 连接用户
prop.put("bootstrap.servers", "node2:9092");
// 创建组号,看是否为新
prop.put("group.id", "ConsumerDome");
// 关闭自动提交
prop.put("enable.auto.commit", "false");
// 设置提交时间,1000要加引号!!!!
// prop.put("auto.commit.interval.ms", "1000");
// 连接
prop.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
prop.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
// offset重设
// 最早的,开头
prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
// 最新的,也就是末尾
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"latest");
// 默认
// prop.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"none");
// 与kafka进行连接,将上面设置的参数添加进取
// 注意是消费者!!!!!
KafkaConsumer<String, String> kafkaConsumer
= new KafkaConsumer<String, String>(prop);
// KafkaProducer<String, String> stringStringKafkaProducer = new KafkaProducer<String, String>(prop);
// 消费者订阅消息,定义集合储存主题
ArrayList<String> topicList = new ArrayList<>();
topicList.add("my_Consumer"); //添加主题
//topiclist.add("my_Consumer"); //没有topic,会创建topic,1分区1副本的topic
// 添加消费者消费的 主题
kafkaConsumer.subscribe(topicList);
// 消费者循环取出数据
while (true){
ConsumerRecords<String, String> poll = kafkaConsumer.poll(Duration.ofSeconds(2));
// 强循环取出数据输出到控制台
for (ConsumerRecord<String, String> ConsumerRecord : poll) {
System.out.println(ConsumerRecord.topic()+"--"+
ConsumerRecord.partition()+"--"+
ConsumerRecord.offset()+"--"+
ConsumerRecord.key()+"--"+
ConsumerRecord.value());
}
/* System.out.println("同步提交offset");
kafkaConsumer.commitAsync();*/
// 异步提交
System.out.println("异步提交offset");
// 调用commitAsync静态方法,设置提交回调函数
kafkaConsumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
if (e == null) {
// 有异常
System.out.println("异步提交成工");
}else {
// 异步提交失败,返回失败原因
System.out.println(e.getMessage());
}
}
});
}
// 一直等待接收,不需要关闭
// kafkaConsumer.close();
}
}
异步:先拉数据,再提交文字
}else {
// 异步提交失败,返回失败原因
System.out.println(e.getMessage());
}
}
});
}
// 一直等待接收,不需要关闭
// kafkaConsumer.close();
}
}
异步:先拉数据,再提交文字
![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=C%3A%5CUsers%5CAdministrator%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5Cimage-20240314190908172.png&pos_id=img-ecL7IcwZ-1713315048031)