如何防止 Kafka 消息在提交过程中丢失?Spring Boot 实战指南

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!


一、问题背景:消息为什么会"丢"?

很多开发者以为 Kafka "天生可靠",但消息丢失往往发生在"消费端提交偏移量"的环节

即使 Kafka 本身持久化了消息,如果你的消费者提前提交了 offset ,而业务逻辑还没执行完,一旦应用崩溃------这条消息就永远消失了

🎯 典型场景:

  • 用户下单成功,Kafka 发送"订单创建"事件
  • 消费者收到消息,准备扣库存
  • offset 被提前提交
  • 扣库存前服务宕机 → 订单已确认,但库存没扣!

这不是 Kafka 的锅,而是提交策略不当导致的!


二、根本原则:先处理业务,再提交 offset

正确顺序

复制代码
1. 拉取消息
2. 执行业务逻辑(如写 DB、调接口)
3. 业务成功 → 提交 offset

错误顺序(自动提交默认行为)

复制代码
1. 拉取消息
2. 后台线程定时提交 offset(不管业务是否完成)
3. 业务执行中崩溃 → 消息丢失

三、解决方案:关闭自动提交 + 手动 ACK + 幂等消费

下面我们用 Spring Boot + Kafka 实现一个不丢消息的消费者。


✅ 步骤 1:关闭自动提交(关键!)

复制代码
# application.yml
spring:
  kafka:
    consumer:
      enable-auto-commit: false     # 必须关闭!
      group-id: order-service
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-desserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      properties:
        spring.json.trusted.packages: "com.example.dto"

⚠️ enable-auto-commit: false 是防止消息丢失的第一道防线!


✅ 步骤 2:配置手动 ACK 模式

复制代码
// KafkaConfig.java
@Configuration
@EnableKafka
public class KafkaConfig {

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, OrderEvent> kafkaListenerContainerFactory(
            ConsumerFactory<String, OrderEvent> consumerFactory) {
        ConcurrentKafkaListenerContainerFactory<String, OrderEvent> factory =
                new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory);
        // 设置为手动立即确认
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
        return factory;
    }
}

✅ 步骤 3:消费者代码:业务成功才 ACK

复制代码
// OrderConsumer.java
@Component
public class OrderConsumer {

    @Autowired
    private InventoryService inventoryService;

    @KafkaListener(topics = "order-created", groupId = "order-service")
    public void consume(OrderEvent event, Acknowledgment ack) {
        try {
            // 1. 业务处理:扣减库存
            inventoryService.decreaseStock(event.getProductId(), event.getQuantity());

            // 2. 业务成功 → 手动提交 offset
            ack.acknowledge(); 

            log.info("订单 {} 处理成功,offset 已提交", event.getOrderId());

        } catch (Exception e) {
            // 3. 业务失败 → 不提交 offset!
            log.error("处理订单失败,不提交 offset,等待重试", e);
            // 可选:记录到死信队列,避免无限重试
        }
    }
}

✅ 这样:只有库存扣减成功,offset 才会提交。如果失败,下次重启还会重新消费同一条消息。


四、进阶保障:幂等性设计(防重复消费)

由于我们采用"失败不提交 offset",消息可能会被重复消费 。因此,消费者必须幂等

🔧 幂等实现方式:

方式 1:数据库唯一索引(推荐)
复制代码
CREATE TABLE processed_messages (
    message_id VARCHAR(64) PRIMARY KEY,  -- Kafka 消息的 key 或生成的 UUID
    processed_at TIMESTAMP
);

消费时:

复制代码
if (!messageLogService.exists(event.getMessageId())) {
    // 执行业务
    inventoryService.decreaseStock(...);
    // 记录已处理
    messageLogService.save(event.getMessageId());
    ack.acknowledge();
}
方式 2:业务状态机
  • 订单状态:CREATEDSTOCK_DEDUCTED
  • 如果已经是 STOCK_DEDUCTED,直接跳过

五、反例对比:自动提交 vs 手动提交

场景 自动提交(enable-auto-commit=true) 手动提交(enable-auto-commit=false)
消费消息后处理耗时 10 秒 5 秒时自动提交 offset 不提交,直到 ack.acknowledge()
处理到第 8 秒时服务崩溃 消息丢失(offset 已提交) 消息保留(offset 未提交,重启后重试)
适合场景 日志、监控等可容忍丢失的数据 订单、支付、通知等关键业务

六、其他注意事项

1. 不要在 ACK 后做关键操作

复制代码
// ❌ 危险!ACK 后再操作,崩溃会导致数据不一致
ack.acknowledge();
inventoryService.decreaseStock(...); // 崩溃 → offset 提交了,但库存没扣!

✅ 正确顺序:业务 → ACK


2. 避免长时间阻塞消费者线程

  • 如果业务耗时很长(如调外部 API),考虑异步处理 + 本地事务表
  • 或使用 @RetryableTopic + 死信队列(DLQ)机制

3. 测试你的容错能力

  • kill -9 模拟非优雅关闭
  • 观察消息是否重试、是否重复、是否丢失

七、总结

要防止 Kafka 消息在提交过程中丢失,请牢记:

  1. 关闭自动提交enable-auto-commit: false
  2. 业务成功后再手动 ACK
  3. 消费者必须幂等(防重复)
  4. ACK 前不要做任何可能失败的关键操作

这样,即使服务崩溃、网络中断、机器宕机,你的消息也不会丢失!

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

相关推荐
岁岁种桃花儿1 小时前
Kafka从入门到上天系列第一篇:kafka的安装和启动
大数据·中间件·kafka
TTBIGDATA1 天前
【Atlas】Atlas Hook 消费 Kafka 报错:GroupAuthorizationException
hadoop·分布式·kafka·ambari·hdp·linq·ranger
indexsunny1 天前
互联网大厂Java面试实战:微服务与Spring生态技术解析
java·spring boot·redis·kafka·mybatis·hibernate·microservices
编程彩机1 天前
互联网大厂Java面试:从Spring Boot到分布式事务的技术场景解析
spring boot·kafka·分布式事务·微服务架构·java面试·技术解析
没有bug.的程序员1 天前
RocketMQ 与 Kafka 深度对垒:分布式消息引擎内核、事务金融级实战与高可用演进指南
java·分布式·kafka·rocketmq·分布式消息·引擎内核·事务金融
yumgpkpm1 天前
华为昇腾300T A2训练、微调Qwen过程,带保姆式命令,麒麟操作系统+鲲鹏CPU
hive·hadoop·华为·flink·spark·kafka·hbase
ApachePulsar1 天前
演讲回顾|谙流科技在 Kafka on Pulsar 之上的探索
分布式·科技·kafka
yumgpkpm2 天前
2026软件:白嫖,开源,外包,招标,晚进场(2025年下半年),数科,AI...中国的企业软件产业出路
大数据·人工智能·hadoop·算法·kafka·开源·cloudera
迎仔2 天前
09-消息队列Kafka介绍:大数据世界的“物流枢纽”
大数据·分布式·kafka
indexsunny2 天前
互联网大厂Java面试实录:Spring Boot微服务与Kafka消息队列实战解析
java·spring boot·微服务·面试·kafka·电商·技术解析