Kafka 消费幂等:重复消息不可怕,重复副作用才可怕

发布时间:2026/7/6 11:05:38
Kafka 消费幂等:重复消息不可怕,重复副作用才可怕 Kafka 消费幂等重复消息不可怕重复副作用才可怕一、Kafka 里重复消费是必须接受的现实Kafka 消费端常见目标是“不要重复消费”。但在真实系统里重平衡、超时、提交 offset 失败、消费者重启都可能导致消息被再次处理。工程上更现实的目标不是完全消灭重复而是让重复消息不会产生重复副作用。比如订单状态更新、积分发放、短信通知、库存扣减这些操作如果没有幂等保护一条消息重复处理就可能造成业务事故。Kafka 本身提供的是消息传递能力业务副作用要靠应用设计兜住。二、消费流程要把处理和提交分开看消息处理成功不代表 offset 已成功提交。flowchart TD A[拉取消息] -- B[解析校验] B -- C[幂等检查] C -- D[执行业务副作用] D -- E[记录处理结果] E -- F[提交 offset] F -- G[下一批消息]如果 D 成功但 F 失败消息可能再次被拉到。幂等检查必须覆盖这种情况。三、业务幂等键要稳定下面是一个简单结构。public record ConsumeCommand( String messageId, String bizId, String eventType, String payloadHash ) {}幂等键可以来自业务 ID、事件类型和版本号。不要只用 Kafka offset因为重发、补偿或跨 topic 流转时 offset 不稳定。四、幂等记录要和业务状态一致常见做法是在数据库里建消费记录表处理前检查处理成功后记录。但要注意事务边界。业务更新成功、幂等记录失败仍然会出问题。最好把业务状态变更和幂等记录放在同一个本地事务里。对于外部副作用比如发短信、调用支付、推送第三方接口要尽量使用对方提供的幂等键。如果对方不支持幂等就要把调用放在更谨慎的补偿流程里。失败消息也要分类。临时失败可以重试永久失败要进入死信队列或人工处理。无限重试会阻塞分区影响后续正常消息。最后监控要看重复率和幂等命中率。重复消息本身不一定是事故但重复率突然升高可能说明消费者不稳定、处理变慢或重平衡频繁。幂等记录也要有生命周期。所有消息永久保存会让表越来越大查询越来越慢。可以按业务保留周期归档或清理但清理前要确认不会再发生补偿重放。保留时间太短历史消息重放时又会失去幂等保护。还要处理消息版本。业务事件升级后同一个 bizId 可能出现不同版本的 payload。幂等键如果只看 bizId就可能误把新版本事件当成重复消息跳过。事件类型和版本号应该进入幂等判断。最后消费者重平衡要减少不必要发生。处理时间过长、心跳配置不合理、批量太大都会触发重复消费风险。幂等是兜底不是放任消费端不稳定的理由。对于高价值事件还可以引入状态机。消息不是简单处理一次而是从 received、processing、succeeded、failed、compensating 等状态流转。状态机比一个布尔幂等标记更适合复杂业务也更方便人工介入。顺序消息要额外小心。如果同一个业务对象的事件必须按顺序处理重复消息和乱序消息要一起治理。幂等只能解决重复不能自动解决旧事件覆盖新状态的问题。最后补偿任务要复用同一套幂等逻辑。很多事故发生在人工补偿脚本或定时重放任务因为它们绕过了正常消费者。只要会产生业务副作用就应该走统一幂等入口。五、总结Kafka 消费端要接受重复消息存在重点是避免重复副作用。稳定幂等键、本地事务、外部调用幂等和失败分类是消费系统可靠性的基础。重复消息不可怕不可控的业务副作用才可怕。