kafka介绍
Kafka 是一个分布式流媒体平台,类似于消息队列或企业消息传递系统。kafka官网:Apache Kafka


-
producer:发布消息的对象称之为主题生产者(Kafka topic producer)
-
topic:Kafka将消息分门别类,每一类的消息称之为一个主题(Topic)
-
consumer:订阅消息并处理发布的消息的对象称之为主题消费者(consumers)
-
broker:已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker)。 消费者可以订阅一个或多个主题(topic),并从Broker拉数据,从而消费这些已发布的消息。
kafka安装配置
Kafka对于zookeeper是强依赖,保存kafka相关的节点数据,所以安装Kafka之前必须先安装zookeeper
kafka移除容器重新开后,想要zookeeper也重新开
Docker安装zookeeper
下载镜像:
docker pull zookeeper:3.4.14
创建容器
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.4.14
Docker安装kafka:下载镜像:
docker pull wurstmeister/kafka:2.12-2.3.1
创建容器
docker run -d --name kafka \
--env KAFKA_ADVERTISED_HOST_NAME=192.168.200.130 \
--env KAFKA_ZOOKEEPER_CONNECT=192.168.200.130:2181 \
--env KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.200.130:9092 \
--env KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \
--env KAFKA_HEAP_OPTS="-Xmx256M -Xms256M" \
--net=host wurstmeister/kafka:2.12-2.3.1
kafka入门

生产者发送消息
(1)创建kafka-demo项目,导入依赖
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
(2)编写消息生产者类ProducerQuickstart
①.设置kafka的配置信息
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.200.130:9092");//消息key的序列化器 连接kafka,只当端口ip
//消息要在网络上运行,使用要对消息的key和value进行序列化
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");//消息value的序列化器
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
②.创建生产者对象
KafkaProducer<String,String> producer = new KafkaProducer<String, String>(properties);
③.发送消息
ProducerRecord<String,String> record = new ProducerRecord<String,String>(“topic","key",“value");producer.send(record);
④.关闭消息通道
producer.close();
代码实现
public class ProducerQuickStart {
public static void main(String[] args) {
// 1.kafka连接的配置信息
Properties prop = new Properties();
// kafka连接地址
// ProducerConfig.BOOTSTRAP_SERVERS_CONFIG相当于"bootstrap.servers",所以这里也可以用这个字符串
prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
// key和value指定序列化器,使用的是kafka的序列化器,在kafka的common包下,全类名为
prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
// 2.创建kafka生产者对象
KafkaProducer<String, String> producer = new KafkaProducer<String, String>(prop);
// 发送消息
/**
* 第一个参数:消息的topic(主题)
* 第二个参数:消息的key
* 第三个参数:消息的value
*/
ProducerRecord<String, String> kvProducerRecord = new ProducerRecord<>("topic-first", "key-001", "hello kafka");
producer.send(kvProducerRecord);
// 4.关闭消息通道,必须要关闭,否则消息发送不成功
producer.close();
}
}
创建ConsumerQuickStart消费者类
①:设置kafka的配置信息
//连接信息
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.200.130:9092");
//指定消费者组
properties.put(ConsumerConfig.GROUP_ID_CONFIG,"group2");
//反序列化的key和value
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringDeserializer");
②:创建消费者对象
KafkaConsumer<String,String> consumer = new KafkaConsumer<String,String>(properties);
③:订阅主题
consumer.subscribe(Collections.singletonList("itcast-001"));
④:获取消息
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
代码实现
public class ConsumerQuickStart {
public static void main(String[] args) {
// 1.kafka的配置信息
Properties prop = new Properties();
// 连接地址
prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
// 消费者的key和value的反序列化器
prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
// 设置消费者组
prop.put(ConsumerConfig.GROUP_ID_CONFIG, "group_1");
// 2.创建消费者对象
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(prop);
// 3.订阅主题topic
consumer.subscribe(Collections.singletonList("topic-first"));
// 4.拉取消息,为了让他一直执行,这里用死循环实现
while (true){
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key());
System.out.println(consumerRecord.value());
}
}
}
}


kafka分区机制

分区,可以理解成是一个存储topic的文件夹,同时发送消息时可以指定topic和分区,也就是让不同数据存储到不同分区下以及不同机器上,增强kafka的伸缩性(p是一个分区,broker一个节点,也叫代理,也叫服务器,t是topic,主题)

每个分区下都维护了一个连续自增的数值来标记消息在什么位置,也叫做偏移量

发消息时,分区策略有三种,默认是轮询,消费者接收消息时,拿到这条消息所在分区
while (true){
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key());
System.out.println(consumerRecord.value());
System.out.println(consumerRecord.partition());
}
}
生产者发送消息时,也可以指定分区,也可以不指定分区,不指定默认是轮询,不写key也是轮询,写了key就是按键保存(计算hashcode值)
// 发送消息
/**
* 第一个参数:消息的topic(主题)
* 第二个参数:分区
* 第三个参数:消息的key
* 第三个参数:消息的value
*/
ProducerRecord<String, String> kvProducerRecord = new ProducerRecord<>("topic-first", 0, "key-001", "hello kafka");
producer.send(kvProducerRecord);
// 4.关闭消息通道,必须要关闭,否则消息发送不成功
producer.close();
kafka面试:
kafka高可用设计:
kafka备份机制

kafka备份机制-同步方式
追随者副本(Follower Replica也有两种,一种是ISR,采用的是同步备份的机制,一种是普通,采用异步备份的机制,都是从Leader Replica中获取)

如果所有副本都失效了,如何解决

如果要保证一致性,选择ISR中第一个活过来的副本做Leader(CP)
如果要保证系统的可用性,选择第一个活过来的副本族谱Leader(AP)
kafka生产者详解
发送类型:同步发送和异步发送
同步发送:
缺点:数据量越大,发送时间就越长,并且还会有阻塞风险,可能会导致服务雪崩
使用send()方法发送,它会返回一个Future对象,调用get()方法进行等待,就可以知道消息是否发送成功
try {
RecordMetadata recordMetadata = producer.send(kvProducerRecord).get();
System.out.println(recordMetadata.offset()); //偏移量,在分区小自增的数值,用于编号消息
} catch (Exception e) {
e.printStackTrace();
}
另一种写法
ProducerRecord<String, String> record =
new ProducerRecord<>("my-topic", "key", "value");
// 调用 .get() 就变成了“同步发送”
Future<RecordMetadata> future = producer.send(record);
RecordMetadata metadata = future.get(); // ⛔ 阻塞等待!
异步发送:
优点:如果消息发送失败,会抛出异常,此时可以保存这个异常到日志表中 ,方便后续分析业务,或者做一些补偿
最好采用异步的方式进行消息的发送,因为不仅可以拿到返回值,还可以记录错误信息,还不容易阻塞
调用send()方法,并指定一个回调函数,服务器在返回响应时调用函数
//消息发送失败,会抛出异常
producer.send(kvProducerRecord, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null){
System.out.println("记录错误信息到日志");
}
System.out.println(metadata.offset());
}
});
另一种写法
ProducerRecord<String, String> record =
new ProducerRecord<>("topic-name", "key", "value");
producer.send(record, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception == null) {
System.out.println("✅ 发送成功,分区:" + metadata.partition() + ", offset:" + metadata.offset());
} else {
System.err.println("❌ 发送失败:" + exception.getMessage());
// 在这里可以做重试、记录日志、告警等
}
}
});

kafka生产者参数
响应参数

acks=0 的配置意味着生产者不会等待任何来自服务器的响应,只要消息被发送出去就认为发送成功了。让我们详细解释一下 acks 参数的不同设置及其影响。
acks=1表示leader返回成功的确认信息生产者就认为发送成功了
一般采用acks = 1
acks=2代表leader要将所有消息同步到所有foller,同步成功leader才会给producer返回一个确认
重试次数

producer发送消息给leader,若leader一直不返回确认消息,重复10次后,若都没有成功,消息发送直接失败
消息压缩
根据实际情况进行选择,在开发者用的最多的事snappy

kafka消费者详解
消费者组

代码:
public class ProducerQuickStart {
public static void main(String[] args) {
// 1.kafka连接的配置信息
Properties prop = new Properties();
// kafka连接地址
// ProducerConfig.BOOTSTRAP_SERVERS_CONFIG相当于"bootstrap.servers",所以这里也可以用这个字符串
prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
// key和value指定序列化器,使用的是kafka的序列化器,在kafka的common包下,全类名为
prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
// 确认机制
prop.put(ProducerConfig.ACKS_CONFIG, "1");
// 重试次数
prop.put(ProducerConfig.RETRIES_CONFIG ,10);
// 数据压缩
prop.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");
// 2.创建kafka生产者对象
KafkaProducer<String, String> producer = new KafkaProducer<String, String>(prop);
// 发送消息
/**
* 第一个参数:消息的topic(主题)
* 第二个参数:分区
* 第三个参数:消息的key
* 第三个参数:消息的value
*/
ProducerRecord<String, String> kvProducerRecord = new ProducerRecord<>("topic-first", 0, "key-001", "hello kafka");
try {
RecordMetadata recordMetadata = producer.send(kvProducerRecord).get();
System.out.println(recordMetadata.offset()); //偏移量,在分区小自增的数值,用于编号消息
} catch (Exception e) {
e.printStackTrace();
}
producer.send(kvProducerRecord, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null){
System.out.println("记录错误信息到日志");
}
System.out.println(metadata.offset());
}
});
// 4.关闭消息通道,必须要关闭,否则消息发送不成功
producer.close();
}
}
消费者组决定了消息传递的模型
queue模型:消息队列,组内只能有一个消费者可以接收到消息(消费者在同一个组)
发布订阅模型:所有消费者都可以拿到消息(消费者在不同组)
消息的有序性

topic分区中消息只能由消费者组中的唯一一个消费者处理,所以消息肯定是按照先后顺序进行处理的。但是它也仅仅是保证Topic的一个分区顺序处理,不能保证跨分区的消息先后处理顺序。
结论
如果你想要顺序的处理Topic的所有消息,那就只提供一个分区。
kafka消费者详解
提交和偏移量


kafka不会像其他JMS队列那样需要得到消费者的确认,消费者可以使用kafka来追踪消息在分区的位置(偏移量) 消费者会往一个叫做_consumer_offset的特殊主题发送消息,消息里包含了每个分区的偏移量。如果消费者发生崩溃或有新的消费者加入群组,就会触发再均衡
再均衡:
当消费者组中的消费者实例发生变化(如新增、退出消费者),Kafka 会触发再均衡过程,重新分配分区给消费者。在再均衡期间,消费者可能会丢失未提交偏移量的消息。
如图所示,如果上一次提交的偏移量为 11,但在再均衡过程中,消费者还在处理偏移量为 5 的消息,那么偏移量 6 到 11 之间的消息将会丢失。


因为实际提交时,都是批量提交,不可能处理一次提交一个
因为频繁提交偏移量 = 性能瓶颈
- 提交偏移量(commit)是一个网络请求,需要和 Kafka 的 Group Coordinator 节点通信。
- 如果每处理一条消息就提交一次,会产生大量网络开销,吞吐量急剧下降。
- 例如:每秒处理 1 万条消息,就要提交 1 万次 offset → 系统几乎瘫痪。
所以实际中是批量处理 + 批量提交
批量提交又会出现下面问题:
多线程下,偏移量提交顺序可能错乱。

- 比如:offset=10 的消息处理快,先提交;offset=5 的还没处理完 → 导致 offset=5~9 的消息“被跳过”。
采用的解决方式
偏移量提交方式
提交偏移量的方式有两种,分别是自动提交偏移量和手动提交
提交当前偏移量(同步提交)
优点:如果提交失败会重试,直到成功或者最后抛出异常
缺点:可能会造成方法的阻塞
while (true){
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key());
System.out.println(consumerRecord.value());
System.out.println(consumerRecord.partition());
// 同步提交
try {
consumer.commitSync();
} catch (CommitFailedException e) {
System.out.println("记录错误信息:"+e);
}
}
}
异步提交
优点:效率高,不需要实时等待
缺点:如果服务器返回提交失败,不会进行重试,如果存在多个异步提交,可能会导致偏移量覆盖
// 异步提交
// 如果提交失败,会执行后面的的逻辑,offsets里面装的就是偏移量
consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
if (exception != null){
System.out.println("记录错误的提交偏移量:"+ offsets+ ",异常信息" + exception);
}
}
});
}
同步和异步组合提交
优点:异步提交失败后,会先去记录日志,最后进行同步提交
try {
while (true) {
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key());
System.out.println(consumerRecord.value());
System.out.println(consumerRecord.partition());
}
// 异步提交
//同步异步组合提交
consumer.commitAsync();
}
} catch (Exception e){
System.out.println("记录错误信息:" + e);
}finally {
try {
consumer.commitSync();
} finally {
consumer.close();
}
}
在写 Kafka 消费者时,我们有三个核心目标:
| 目标 | 说明 |
|---|---|
| ✅ 不丢消息 | 即使程序崩溃,重启后也不能跳过未处理的消息 |
| ✅ 不重复消费太多 | 已处理的消息不要反复处理(影响性能) |
| ✅ 性能要好 | 不能因为“提交偏移量”拖慢整个消费速度 |
“平时异步,最后同步”
这就是你代码的精髓:
📌 平时:commitAsync() → 要快
- 正常运行时,用异步提交,保证消费速度。
- 就像“批量发快递,不等签收回执”。
📌 最后:finally 里 commitSync() → 要稳
- 程序关闭前,必须确保最后一次提交成功。
- 就像“关门之前,一定要确认所有重要文件都寄出去了”。
“偏移量覆盖”不是异步提交(commitAsync)的 bug,而是 Kafka 的正常行为 —— 后提交的 offset 会覆盖先提交的,无论它是同步还是异步。
技术上:Kafka 怎么存 offset?
Kafka 把 offset 存在一个叫 __consumer_offsets 的内部 topic 中,它是一个 key-value 存储:
| Key(消费者组 + topic + partition) | Value(offset 值) |
|---|---|
group1-topicA-0 | 100 |
当你调用:
consumer.commitAsync(); // 提交 offset=100
Kafka 会:
- 找到 key:
group1-topicA-0 - 把 value 更新为
100
下一次你提交 offset=120:
consumer.commitAsync(); // 提交 offset=120
Kafka 会:
- 找到同一个 key
- 把 value 更新为
120(覆盖100)
✅ 所以“覆盖”是正常更新,不是重复或错误。
为什么“异步”更容易让人觉得“覆盖有问题”?
因为 commitAsync() 是 异步的、不保证顺序的,可能会出现“乱序提交”问题。
❌ 举例:异步提交的“乱序”风险
// 第一批消息:offset=0~99
consumer.commitAsync(); // 提交 offset=100(请求已发出,但还没到 Kafka)
// 第二批消息:offset=100~119
consumer.commitAsync(); // 提交 offset=120(这个请求可能先到 Kafka)
可能出现:
- offset=120 的提交先到达 Kafka → 记录为 120
- offset=100 的提交后到达 → 覆盖成 100 ❌
👉 结果:Kafka 记录的 offset 反而变小了!
如果此时消费者重启,会从 offset=100 开始消费 → offset=100~119 的消息被重复消费
⚠️ 这才是“异步提交导致覆盖”的真正风险!
如何避免“乱序覆盖”?
方法 1:不要频繁异步提交
- 批量处理一批消息后提交一次,减少提交次数
- 避免在多线程中随意提交
方法 2:使用回调控制顺序(不推荐)
consumer.commitAsync((offsets, exception) -> {
// 只有当前提交成功后,才允许下一次提交
});
但这样会降低性能,失去异步意义。
方法 3:靠 finally 中的 commitSync() 来兜底
- 即使中间
commitAsync()乱序了,只要最后commitSync()成功提交最新 offset,就不会丢数据
总结:为什么“异步会产生偏移量覆盖”?
| 问题 | 回答 |
|---|---|
| 异步提交会导致 offset 被覆盖吗? | ✅ 会,但这是正常行为 —— 新值覆盖旧值 |
| 这种覆盖有问题吗? | 一般没问题,但如果后提交的先到、先提交的后到(乱序),可能导致 offset 回退 → 重复消费 |
| 如何避免风险? | 减少异步提交次数 + 最后用 commitSync() 确保最终进度 |
| “覆盖”是 bug 吗? | ❌ 不是!是 Kafka 的正常更新机制 |
最后一句话:
“偏移量覆盖”不是异步的问题,而是“状态更新”的本质。
异步提交的风险不是“覆盖”,而是“可能乱序提交,导致状态回退”。
所以我们用commitSync()在最后“一锤定音”,确保最终进度正确。
完整版代码:
生产者
package com.heima.kafka.simple;
import org.apache.kafka.clients.producer.*;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
/**
* @author NoFear
* @version 1.0
* @description: TODO
* @date 2025/8/21 2:26
*/
public class ProducerQuickStart {
public static void main(String[] args) {
// 1.kafka连接的配置信息
Properties prop = new Properties();
// kafka连接地址
// ProducerConfig.BOOTSTRAP_SERVERS_CONFIG相当于"bootstrap.servers",所以这里也可以用这个字符串
prop.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
// key和value指定序列化器,使用的是kafka的序列化器,在kafka的common包下,全类名为
prop.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
// 确认机制
prop.put(ProducerConfig.ACKS_CONFIG, "1");
// 重试次数
prop.put(ProducerConfig.RETRIES_CONFIG ,10);
// 数据压缩
prop.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, "snappy");
// 2.创建kafka生产者对象
KafkaProducer<String, String> producer = new KafkaProducer<String, String>(prop);
// 发送消息
/**
* 第一个参数:消息的topic(主题)
* 第二个参数:分区
* 第三个参数:消息的key
* 第三个参数:消息的value
*/
ProducerRecord<String, String> kvProducerRecord = new ProducerRecord<>("topic-first", 0, "key-001", "hello kafka");
try {
RecordMetadata recordMetadata = producer.send(kvProducerRecord).get();
System.out.println(recordMetadata.offset()); //偏移量,在分区小自增的数值,用于编号消息
} catch (Exception e) {
e.printStackTrace();
}
//异步发送
producer.send(kvProducerRecord, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null){
System.out.println("记录错误信息到日志");
}
System.out.println(metadata.offset());
}
});
// 4.关闭消息通道,必须要关闭,否则消息发送不成功
producer.close();
}
}
消费者
package com.heima.kafka.simple;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.TopicPartition;
import java.time.Duration;
import java.util.Calendar;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
/**
* @author NoFear
* @version 1.0
* @description: TODO
* @date 2025/8/21 2:25
*/
public class ConsumerQuickStart {
public static void main(String[] args) {
// 1.kafka的配置信息
Properties prop = new Properties();
// 连接地址
prop.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
// 消费者的key和value的反序列化器
prop.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
prop.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
// 设置消费者组
prop.put(ConsumerConfig.GROUP_ID_CONFIG, "group_1");
// 2.创建消费者对象
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(prop);
// 3.订阅主题topic
consumer.subscribe(Collections.singletonList("topic-first"));
// 4.拉取消息,为了让他一直执行,这里用死循环实现
try {
while (true) {
ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key());
System.out.println(consumerRecord.value());
System.out.println(consumerRecord.partition());
}
// 异步提交
// 如果提交失败,会执行后面的的逻辑,offsets里面装的就是偏移量
//同步异步组合提交
consumer.commitAsync();
}
} catch (Exception e){
System.out.println("记录错误信息:" + e);
}finally {
try {
consumer.commitSync();
} finally {
consumer.close();
}
}
/* consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
if (exception != null){
System.out.println("记录错误的提交偏移量:"+ offsets+ ",异常信息" + exception);
}
}
});
}
}*/
// while (true){
// ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
// for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
// System.out.println(consumerRecord.key());
// System.out.println(consumerRecord.value());
// System.out.println(consumerRecord.partition());
//// 同步提交
// try {
// consumer.commitSync();
// } catch (CommitFailedException e) {
// System.out.println("记录错误信息:"+e);
// }
// }
// }
}
}
1429

被折叠的 条评论
为什么被折叠?



