Kafka消息重复消费与幂等性保证
Kafka中为什么会出现消息重复消费(Consumer Rebalance、自动提交offset、业务处理时间过长)?如何保证消费的幂等性?请介绍至少三种幂等实现方案。
回答
苦行僧
重复消费原因:
- Consumer Rebalance:分区重新分配时,已拉取但未提交offset的消息被重复消费
- 自动提交offset:enable.auto.commit=true,业务处理过程中崩溃,offset已提交但业务未完成
- 手动提交失败:commitSync抛出异常,下次拉取从头开始
- 生产者重试:producer发送消息后Broker写入成功但ack丢失,生产者重试导致消息重复
幂等性保证方案:
方案1:数据库唯一约束
// 插入业务主键去重
INSERT INTO order (order_id, ...) VALUES (?, ...)
ON DUPLICATE KEY UPDATE ...
// 或使用INSERT ... WHERE NOT EXISTS
方案2:Redis分布式锁 + 去重表
// 消费前检查是否已处理
String key = "consumer:" + msgId;
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.HOURS);
if (Boolean.TRUE.equals(success)) {
// 执行业务逻辑
process(message);
} else {
// 已处理过,跳过
log.info("消息重复,跳过: {}", msgId);
}
方案3:Kafka生产者幂等性
# 生产者配置
enable.idempotence=true
# 配合acks=all + retries>0使用
# 原理:每个Producer绑定Producer ID + 序列号,Broker去重
方案4:状态机校验
// 利用业务状态机,判断当前状态是否允许消费
// 如订单已支付,则忽略支付回调消息
switch (order.getStatus()) {
case UNPAID: // 处理支付成功逻辑
case PAID: // 已支付,忽略
}
推荐:优先使用业务数据库唯一索引保证幂等,简单可靠。再配合Kafka自身幂等性生产者减少服务端重复。