一、前言
数据重复这个问题其实也是挺正常,全链路都有可能会导致数据重复。
通常,消息消费时候都会设置一定重试次数来避免网络波动造成的影响,同时带来副作用是可能出现消息重复。
整理下消息重复的几个场景:
-
生产端: 遇到异常,基本解决措施都是 重试 。
-
场景一:
leader
分区不可用了,抛LeaderNotAvailableException
异常,等待选出新leader
分区。 -
场景二:
Controller
所在Broker
挂了,抛NotControllerException
异常,等待Controller
重新选举。 -
场景三:网络异常、断网、网络分区、丢包等,抛
NetworkException
异常,等待网络恢复。
-
消费端:
poll
一批数据,处理完毕还没提交offset
,机子宕机重启了,又会poll
上批数据,再度消费就造成了消息重复。
怎么解决?
先来了解下消息的三种投递语义:
-
最多一次(
at most once
): 消息只发一次,消息可能会丢失,但绝不会被重复发送。例如:mqtt
中QoS = 0
。 -
至少一次(
at least once
): 消息至少发一次,消息不会丢失,但有可能被重复发送。例如:mqtt
中QoS = 1
-
精确一次(
exactly once
): 消息精确发一次,消息不会丢失,也不会被重复发送。例如:mqtt
中QoS = 2
。
了解了这三种语义,再来看如何解决消息重复,即如何实现精准一次,可分为三种方法:
-
Kafka
幂等性Producer
: 保证生产端发送消息幂等。局限性,是只能保证单分区且单会话(重启后就算新会话) -
Kafka
事务: 保证生产端发送消息幂等。解决幂等Producer
的局限性。 -
消费端幂等:保证消费端接收消息幂等。蔸底方案。
1)Kafka
幂等性 Producer
幂等性指 :无论执行多少次同样的运算,结果都是相同的。即一条命令,任意多次执行所产生的影响均与一次执行的影响相同。
幂等性使用示例:在生产端添加对应配置即可
Properties props = new Properties();
props.put("enable.idempotence", ture); // 1. 设置幂等
props.put("acks", "all"); // 2. 当 enable.idempotence 为 true,这里默认为 all
props.put("max.in.flight.requests.per.connection", 5); // 3. 注意
-
设置幂等,启动幂等。
-
配置
acks
,注意:一定要设置acks=all
,否则会抛异常。 -
配置
max.in.fl