You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Kafka消费者拉取消息后,删除主题消息仍能读取全部?

问题描述

我有一个从Kafka读取消息的Spring Boot应用,遇到了无法理解的行为。

配置信息

spring:
  kafka:
    consumer:
      max-poll-records: 5
    listener:
      ack-mode: record
      concurrency: 1
logging:
  level:
    org.springframework.kafka.listener.KafkaMessageListenerContainer: DEBUG

日志现象

从日志中可以看到消息以每次5条的批量形式被读取:

2023-08-04T13:21:25.053+02:00 DEBUG [,] 12001 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : Received: 5 records
//Handling first 4 messages
2023-08-04T13:21:57.114+02:00  INFO [,] 12001 --- [ntainer#0-0-C-1] SomeListener: Handling command message

2023-08-04T13:22:05.134+02:00 DEBUG [,] 12001 --- [ntainer#0-0-C-1] o.s.k.l.KafkaMessageListenerContainer    : Received: 5 records
2023-08-04T13:22:05.135+02:00  INFO [,] 12001 --- [ntainer#0-0-C-1] SomeListener: Handling command message
//Handling next 4 messages

测试场景

我向主题发送了200条消息,在处理开始后,通过Kafka-UI以及设置retention.ms的方式删除了这些消息,以此验证应用是否真的每次拉取5条消息。但即便主题中已无消息,应用仍能读取全部200条消息。

请问这属于Kafka的正常行为吗?


回答

这是Kafka消费者的正常行为,核心原因在于Kafka消费者客户端的本地缓存机制:

  • Kafka原生消费者客户端(Spring Kafka底层依赖它)会在后台从Broker拉取消息并存储在本地内存缓存中,供应用通过poll()方法获取。你配置的max-poll-records:5只是限制每次poll()返回给应用的消息条数,而非限制客户端从Broker拉取的总条数。
  • 原生客户端默认有fetch.max.records=500的配置(可通过spring.kafka.consumer.fetch-max-records修改),这表示每次从Broker拉取的消息上限是500条。当你向主题发送200条消息后,消费者客户端会在处理初期就把这200条消息全部拉取到本地缓存中,后续即使Broker上的消息被删除,应用依然可以从本地缓存中读取并处理剩余消息。
  • 你设置的ack-mode:record只是控制处理完单条消息后向Broker提交offset,和消息的拉取、本地缓存逻辑无关。只要消息已经被拉取到客户端本地,无论Broker上的消息是否存在,都会被处理完成。

如果想要验证“每次仅拉取5条”的逻辑,需要同时限制fetch.max.records=5和max-poll-records=5,这样客户端每次只能从Broker拉取5条,处理完这5条后才会再次拉取下一批。此时如果在处理中途删除Broker上的剩余消息,应用就无法继续读取到后续消息了。

内容的提问来源于stack exchange,提问作者ketrab321

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 02:46:09