加群联系作者vx:xiaoda0423
仓库地址:https://webvueblog.github.io/JavaPlusDoc/
👉 Kafka Producer 端(生产者)配置
👉 Kafka Consumer 端(消费者)配置
区分了不同的消费模式 (批量消费、单条消费),并且生产者也是分别独立管理。
🛠️ 流程步骤概览
阶段 | 内容 |
---|---|
配置阶段 | 通过 @Configuration 注解注册生产者、消费者配置。 |
属性注入 | 通过 @Value 注解注入 application.yml 或 application.properties 中的 Kafka 配置。 |
Bean 定义 | 生产者定义 KafkaTemplate ,消费者定义 KafkaListenerContainerFactory (分批量和单条消费两种)。 |
使用阶段 | 在业务代码中注入对应的 KafkaTemplate 发送消息,注解 @KafkaListener 配合不同的 ContainerFactory 接收消息。 |
📈 数据结构变化过程
阶段 | 数据结构 | 变化说明 |
---|---|---|
生产者发送前 | Java对象/String | Java对象通常先转为 String(通过序列化器) |
传输到 Kafka | byte[] | Kafka 内部只认识字节数组 |
消费者接收 | byte[] → String | 消费者接收到字节流,反序列化回 String |
消费逻辑处理 | Java对象/String | 再根据业务场景反序列化成 Java 对象处理 |
📚 示例操作流程(完整链路)
1. 启动阶段
- Spring Boot 扫描到
@Configuration
,注册 KafkaTemplate 和 KafkaListenerContainerFactory。
2. 发送消息(生产者)
go
@Autowired
@Qualifier("kafkaTemplate")
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessage(String topic, String message) {
kafkaTemplate.send(topic, message);
}
3. 消费消息(消费者)
go
@KafkaListener(topics = "test-topic", containerFactory = "kafkaBRConsumerFactory")
public void listenBatch(List<ConsumerRecord<String, String>> records) {
for (ConsumerRecord<String, String> record : records) {
System.out.println("Received message: " + record.value());
}
}
// 或者单条消费
@KafkaListener(topics = "test-topic", containerFactory = "kafkaOBORConsumerFactory")
public void listenSingle(ConsumerRecord<String, String> record) {
System.out.println("Received single message: " + record.value());
}
-
生产者和消费者配置完全隔离,职责清晰。
-
支持批量消费 (性能高)和逐条消费(处理更精细)两种模式。
-
KafkaTemplate 统一封装消息发送,内部使用的是 ProducerFactory。
-
KafkaListenerContainerFactory 统一封装消息监听,内部使用的是 ConsumerFactory。
-
生产消费流程:Java对象 → Kafka字节流传输 → Java对象。
📄 1. application.yml
Kafka配置模板
go
kafka:
producer:
servers: 127.0.0.1:9092
retries: 3 # 发送失败重试次数
batch:
size: 16384 # 批量发送最大字节数
linger: 1 # 等待时间(ms)
buffer:
memory: 33554432 # 32MB缓冲区大小
consumer:
servers: 127.0.0.1:9092
enable:
auto:
commit: false # 是否自动提交offset
session:
timeout: 30000 # session超时时间
auto:
commit:
interval: 1000 # 自动提交offset时间间隔
group:
id: my-consumer-group
auto:
offset:
reset: latest # 最新位置消费(可选 earliest)
concurrency: 3 # 并发线程数
注意:
-
producer.servers
和consumer.servers
通常是一样的(Kafka 集群地址)。 -
auto.offset.reset
: -
-
latest
:只消费启动后新的消息 -
earliest
:从最早可用的消息开始消费(适合首次启动的消费者)
-
🚀 2. 消费者端:加上消息重试机制
在你的消费者容器工厂上,加个 SeekToCurrentErrorHandler
,遇到异常能自动重试N次(否则Kafka默认抛出异常整个消费线程就挂掉了!)
go
private KafkaListenerContainerFactory<?> getConsumerFactory(boolean batchListener) {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(concurrency);
factory.setBatchListener(batchListener);
// 消息处理异常时重试配置(重要)
factory.setErrorHandler(new SeekToCurrentErrorHandler(
new FixedBackOff(5000L, 3) // 5秒重试一次,总共重试3次
));
return factory;
}
细节说明:
-
SeekToCurrentErrorHandler
会把这条消息重新投到消费队列中,不会丢失。 -
FixedBackOff(5000L, 3)
:表示每隔5秒重试一次 ,重试3次后如果还失败就记录error日志。
如果你想更极致一点,比如:重试 N 次还失败就发到 死信队列(DLQ) ,也可以配!
⚡ 3. 生产者端:Kafka发送消息异步带回调
别直接 kafkaTemplate.send()
完就不管了,加一个Callback回调,拿到真正的发送成功 or 失败,做链路监控。
补充一下发送方法:
go
@Autowired
@Qualifier("kafkaTemplate")
private KafkaTemplate<String, String> kafkaTemplate;
public void sendMessageWithCallback(String topic, String message) {
kafkaTemplate.send(topic, message).addCallback(
success -> {
if (success != null) {
System.out.println("发送成功!topic:" + success.getRecordMetadata().topic() +
" partition:" + success.getRecordMetadata().partition() +
" offset:" + success.getRecordMetadata().offset());
}
},
failure -> {
System.err.println("发送失败!原因:" + failure.getMessage());
}
);
}
说明:
-
success
可以拿到:topic、partition、offset,链路追踪好用! -
failure
可以拿到异常信息,比如超时、网络问题、队列满等。
✅ 总结(目前你的Kafka模块)
组件 | 细节 |
---|---|
Producer | 配置 ProducerFactory、KafkaTemplate,支持异步Callback |
Consumer | 配置 ConsumerFactory、ListenerContainerFactory,支持批量、逐条消费、异常重试 |
配置文件 | application.yml统一管理 |
错误处理 | 加了 SeekToCurrentErrorHandler 自动重试 |
发送保障 | 生产者发送加回调,失败报警 |
🚀 你可以直接拿来实战用,真正可以扛流量的 Kafka 消费生产链路!
🧩 1. 死信队列处理机制整体流程
✅ 正常消费 →
❌ 消费失败 + 重试多次 →
🔁 还是失败 →
➡️ 将原消息投递到【死信Topic】(Dead Letter Topic)
➡️ 后续人工修复 or 自动补偿
🛠️ 2. 代码实现分三步
(1)修改你的 getConsumerFactory
方法,加 DeadLetterPublishingRecoverer
我们要替换掉原来的 SeekToCurrentErrorHandler
,换成支持死信队列的异常处理器:
go
@Autowired
@Qualifier("kafkaTemplate") // 用来发到死信topic
private KafkaTemplate<String, String> kafkaTemplate;
private KafkaListenerContainerFactory<?> getConsumerFactory(boolean batchListener) {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setConcurrency(concurrency);
factory.setBatchListener(batchListener);
// 配置死信队列处理器
DeadLetterPublishingRecoverer recoverer = new DeadLetterPublishingRecoverer(kafkaTemplate,
(r, e) -> new TopicPartition(r.topic() + ".DLT", r.partition())
// 失败后投递到原Topic后缀加.DLT(Dead Letter Topic)
);
// 失败后重试3次,每次间隔5秒,重试完仍失败则交给recoverer处理
factory.setErrorHandler(new SeekToCurrentErrorHandler(recoverer, new FixedBackOff(5000L, 3)));
return factory;
}
🔵 解释一下:
-
DeadLetterPublishingRecoverer
:失败后自动把消息重新生产到.DLT
结尾的新Topic,比如消费order-event
失败了,就投到order-event.DLT
-
FixedBackOff(5000, 3)
:5秒重试1次,总共重试3次 -
如果3次都失败,就触发
recoverer
,把消息扔到死信队列里。
(2)application.yml 配置补充(DLQ Topic创建)
Kafka死信队列是一个普通的Topic,只不过你最好提前建好(也可以用自动创建)。
比如:
go
kafka:
topics:
- name: order-event
- name: order-event.DLT # 死信队列
如果你用的 Kafka Server 开了自动建Topic(auto.create.topics.enable=true),也可以不手动建。
(3)写个死信队列监听器(DLT Consumer)
比如你可以新建一个 DLQ 处理器,专门监听死信消息:
go
@Component
public class DeadLetterConsumer {
@KafkaListener(topics = "order-event.DLT", groupId = "dead-letter-group")
public void consumeDeadLetter(String message) {
System.err.println("【死信消息处理】收到消息:" + message);
// TODO: 这里可以发告警、落库、人工介入、补偿逻辑等
}
}
注意:这里可以记录日志、发告警(钉钉/飞书/邮件),或者存到DB里,方便后续人工修复。
🎯 3. DLQ机制总结图
go
正常消费成功 --> 直接ack
正常消费失败 --> 重试 --> 成功ack
重试N次失败 --> DeadLetterPublishingRecoverer --> 投递到 死信Topic
死信消费者监听 --> 告警 or 补偿处理
🔥 总结
步骤 | 内容 |
---|---|
1 | 给 KafkaListenerContainerFactory 加上 DeadLetterPublishingRecoverer |
2 | 配好死信Topic(一般就是正常Topic后加 .DLT ) |
3 | 写一个专门的死信消费者 |
4 | 死信消息可以告警、存库、人工补偿 |
这样配置下来,你的 Kafka 消费链路就非常稳定了:不会因为单条脏数据影响整体消费系统健康。
-
死信消息里最好加上:
-
-
原始消息体
-
消费失败原因(异常栈)
-
时间戳
-
-
死信消费可以打到链路追踪系统,比如接入 Skywalking/Zipkin
-
对重要消息类型可以设置死信消息补偿重试机制
🚀 每个Bean的作用详细解释
Bean | 作用 |
---|---|
ServerEndpointExporter |
核心 !Spring Boot 启动时会扫描 @ServerEndpoint 注解的类,自动注册成 WebSocket 端点。没有它的话,@ServerEndpoint 注解不会生效! |
BaseEndpointConfigure |
扩展配置 ,通常用来自定义 WebSocket 握手过程,比如:校验token、设置自定义属性、统一Session管理、拦截器(handshake拦截)等。 |
🔥 WebSocket启动内部执行流程
-
Spring Boot 容器启动 →
-
扫描到
@Configuration
注解的WebSocketConfig
→ -
创建
ServerEndpointExporter
实例,注册到 Spring 容器中 → -
ServerEndpointExporter
开始扫描项目中所有@ServerEndpoint
注解的类 → -
找到后,自动将这些类注册成 WebSocket端点(endpoint) →
-
创建
BaseEndpointConfigure
Bean,供你后续扩展(比如你可以写自己的modifyHandshake
)