SpringBoot 接入 Kafka 完整实操步骤
一、前置环境准备
-
启动 Kafka 服务 (本地测试,KRaft 或 Zookeeper 模式均可,默认端口
9092)- 本地快速测试可用
docker-compose拉起单节点 Kafka - 确认
server.properties中advertised.listeners=PLAINTEXT://localhost:9092,外网访问改为对应公网 IP
- 本地快速测试可用
-
提前确认 Topic(可以代码自动创建,也可以命令行手动创建)
手动创建topic示例
bin/kafka-topics.sh --create --topic demo-topic --bootstrap-server localhost:9092 --partitions 3 --replication-factor 1
二、引入 Maven 依赖
SpringBoot2.x:推荐
spring-boot-starter-kafka;SpringBoot3.x 直接使用spring-kafka(starter 已移除),不用写版本,父工程自动管理兼容版本
<!-- SpringBoot 2.x -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-kafka</artifactId>
</dependency>
<!-- SpringBoot 3.x -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<!-- 可选:web用于测试接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
三、配置文件 application.yml(核心)
基础最简配置(字符串收发,开发调试)
spring:
kafka:
# kafka集群地址,多个用逗号分隔
bootstrap-servers: localhost:9092
# 生产者配置
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.apache.kafka.common.serialization.StringSerializer
acks: 1 # 调试用;生产重要业务改为 all
retries: 3
# 消费者配置
consumer:
group-id: demo-consumer-group # 消费者组,必须配置
auto-offset-reset: earliest # 新组没有offset时,从头消费
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
enable-auto-commit: true # 调试自动提交;生产建议关闭手动提交
listener:
concurrency: 3 # 消费并发线程,建议 ≤ topic分区数
✅ 生产推荐可靠配置(关闭自动提交、JSON 序列化)
spring:
kafka:
bootstrap-servers: localhost:9092
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
acks: all
retries: 3
enable-idempotence: true # 幂等生产者,防重复发送
consumer:
group-id: demo-consumer-group
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
enable-auto-commit: false # 关闭自动提交,业务成功后手动ack
max-poll-records: 50
properties:
spring.json.trusted.packages: "*" # JSON反序列化信任所有包
listener:
ack-mode: MANUAL_IMMEDIATE # 手动立即提交offset
concurrency: 3
四、代码实现
1. 生产者(发送消息,KafkaTemplate)
SpringBoot 自动注入 KafkaTemplate,直接 Autowired 使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class KafkaProducerController {
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
private static final String TOPIC_NAME = "demo-topic";
// 异步发送(推荐,非阻塞)
@GetMapping("/send")
public String sendMsg(@RequestParam String msg) {
kafkaTemplate.send(TOPIC_NAME, msg).addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
@Override
public void onSuccess(SendResult<String, Object> result) {
System.out.println("发送成功:offset=" + result.getRecordMetadata().offset());
}
@Override
public void onFailure(Throwable ex) {
System.err.println("发送失败:" + ex.getMessage());
}
});
return "发送完成:" + msg;
}
// 同步发送(阻塞,适合强一致性场景)
/*
public void syncSend(String msg) throws ExecutionException, InterruptedException {
SendResult<String, Object> sendResult = kafkaTemplate.send(TOPIC_NAME, msg).get();
}
*/
}
2. 消费者(@KafkaListener 监听消息)
方式 1:基础消费(自动提交,调试)
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
public class KafkaConsumer {
@KafkaListener(topics = "demo-topic", groupId = "demo-consumer-group")
public void consume(ConsumerRecord<String, Object> record) {
System.out.printf("收到消息:topic=%s, partition=%d, offset=%d, message=%s%n",
record.topic(), record.partition(), record.offset(), record.value());
}
}
方式 2:手动提交 offset(生产推荐,保证业务成功再提交)
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;
@Component
public class KafkaManualConsumer {
@KafkaListener(topics = "demo-topic", groupId = "demo-consumer-group")
public void consume(ConsumerRecord<String, Object> record, Acknowledgment ack) {
try {
// 执行业务逻辑
System.out.println("业务处理消息:" + record.value());
// 业务成功,手动提交offset
ack.acknowledge();
} catch (Exception e) {
// 业务异常:不提交offset,下次重启重新消费;可搭配重试/死信队列
e.printStackTrace();
}
}
}
3. 【可选】代码自动创建 Topic(项目启动不存在则创建)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.config.TopicBuilder;
import org.springframework.kafka.core.NewTopic;
@Configuration
public class KafkaTopicConfig {
@Bean
public NewTopic demoTopic() {
// topic名称、分区数、副本数
return TopicBuilder.name("demo-topic")
.partitions(3)
.replicas(1)
.build();
}
}
五、测试流程
- 启动 SpringBoot 应用
- 浏览器访问接口:
http://localhost:8080/send?msg=hello-kafka - 查看控制台消费者打印消息,验证收发正常
六、进阶常用方案
- 批量消费 :容器工厂开启
setBatchListener(true),监听方法接收List<ConsumerRecord> - 消息重试 + 死信队列 :Spring-Kafka 提供
@RetryableTopic+@DltHandler注解,异常消息重试耗尽转入 DLT 死信主题CSDN博... - 对象序列化 :使用
JsonSerializer / JsonDeserializer,注意信任包配置 - 事务消息:生产者开启事务,实现「数据库操作 + 发消息」原子一致性
- 幂等消费:Kafka 至少一次投递,业务层必须做幂等(唯一 key 去重)
七、常见踩坑
- 连接超时 :
bootstrap-servers地址错误、防火墙 9092 端口未开放、advertised.listeners配置内网地址外部无法访问 - 序列化异常 :生产者和消费者序列化器不一致;JSON 序列化未配置
spring.json.trusted.packages - 重复消费:业务处理成功后应用崩溃,offset 未提交 → 关闭自动提交 + 业务幂等
- 消费阻塞积压 :
concurrency并发数大于分区数无效;业务处理耗时过长超过max.poll.interval.ms触发 rebalance - 同一消费者组多实例负载均衡:分区数量决定最大并行消费能力,实例超过分区数会空闲
八、版本兼容提醒
- SpringBoot 2.7.x → spring-kafka 2.9.x
- SpringBoot 3.0.x → spring-kafka 3.0.x
- SpringBoot 3.1.x → spring-kafka 3.1.x
- SpringBoot 3.2.x → spring-kafka 3.2.x