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
相关产品推荐
相关产品推荐

