
当 kafka 消费者禁用自动提交(enable.auto.commit=false)且未手动提交偏移量时,消息不会被标记为已处理;重启消费者或发生再平衡后,同一消息将被重新拉取,从而实现“至少一次”语义下的重投递。
当 kafka 消费者禁用自动提交(enable.auto.commit=false)且未手动提交偏移量时,消息不会被标记为已处理;重启消费者或发生再平衡后,同一消息将被重新拉取,从而实现“至少一次”语义下的重投递。
在您提供的配置中:
- enable.auto.commit=false:关闭自动提交,偏移量需显式调用 commitSync() 或 commitAsync() 才会持久化;
- max.poll.records=1:每次 poll 最多拉取 1 条消息,有利于细粒度控制处理与提交节奏;
- auto.offset.reset=latest:仅在无有效提交偏移量时从最新位置开始消费(即启动时若无历史 offset,则跳过历史消息);
- group.id="processor-1":所有同组消费者共享消费进度(offset),由 Group Coordinator 统一协调。
关键行为说明如下:
✅ 单消费者场景:
只要未调用 consumer.commitSync()(或 commitAsync()),该消费者的当前消费位置(offset)就不会更新。一旦进程重启(或因超时触发再平衡),Kafka 将依据 group 内最后提交的 offset(此处始终为初始值或上一次成功提交值)重新分配分区,并从该 offset 开始拉取消息——因此未 ack 的消息会在重启后重复出现。注意:Kafka 本身不会主动重发未确认消息;它只是“按提交的 offset 拉取”,而由于 offset 未更新,下次仍会拉到相同消息。
✅ 多消费者同组场景(如 consumer-1 和 consumer-2):
若 consumer-1 拉取了消息 A(partition X, offset Y),但未提交 offset,随后因崩溃、GC 停顿超时(session.timeout.ms 默认 45s)或主动关闭导致其退出组,Kafka 会触发再平衡(rebalance)。此时 partition X 将被重新分配给 consumer-2。由于 consumer-1 从未提交 offset Y,Group Coordinator 中记录的该分区的 committed offset 仍为 Y−1(或更早值),因此 consumer-2 将从 offset Y 开始消费——消息 A 会被 consumer-2 再次接收。
⚠️ 注意事项:
- session.timeout.ms(默认 45s)和 heartbeat.interval.ms(默认 3s)共同决定消费者存活感知精度。若处理逻辑耗时 > session.timeout.ms 且未及时发送心跳,Kafka 会认为该消费者已失联并触发再平衡。
- max.poll.interval.ms(默认 5 分钟)限制两次 poll() 调用的最大间隔。若业务处理时间过长(如阻塞 I/O、复杂计算),超过该阈值也会导致消费者被踢出群组。
- 单条消息处理 + 不提交 offset 的模式虽便于调试,但在生产环境易引发重复消费,建议遵循“处理完成 → 提交 offset”原子性原则,或采用幂等写入/去重设计。
示例:安全的手动提交模式(带异常保护)
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
for (ConsumerRecord<String, String> record : records) {
try {
process(record); // 业务逻辑(应尽量快,避免超时)
// 成功处理后同步提交当前 offset
consumer.commitSync(Collections.singletonMap(
new TopicPartition(record.topic(), record.partition()),
new OffsetAndMetadata(record.offset() + 1)
));
} catch (Exception e) {
logger.error("Failed to process record", e);
// 可选择跳过该消息或抛出异常终止循环
break;
}
}
}总结:Kafka 本身不提供“未 ack 消息定时重投”机制(区别于 RabbitMQ 的 nack + TTL),其重投递完全依赖消费者组再平衡 + offset 提交状态缺失。因此,是否重复消费,取决于你的提交时机、消费者稳定性及组协调策略——理解并合理配置 session.timeout.ms、max.poll.interval.ms 和提交逻辑,是保障 Exactly-Once 或 At-Least-Once 语义的关键基础。


















