一、生产者阶段重复场景

### ---消息重复的场景及解决方案

~~~ # 消息重复和丢失是kafka中很常见的问题,主要发生在以下三个阶段:
~~~ 生产者阶段
~~~ broke阶段
~~~ 消费者阶段
### --- 根本原因

~~~ 生产发送的消息没有收到正确的broke响应,导致生产者重试。
~~~ 生产者发出一条消息,
~~~ broke落盘以后因为网络等种种原因发送端得到一个发送失败的响应或者网络中断,
~~~ 然后生产者收到一个可恢复的Exception重试消息导致消息重复。

二、重试过程

|NO.Z.00080|——————————|BigDataEnd|——|Hadoop&kafka.V65|——|kafka.v65|消息重复场景|解决方案.v01|_kafka

### ---说明:

~~~ new KafkaProducer()后创建一个后台线程KafkaThread扫描RecordAccumulator中是否有消息;
~~~ 调用KafkaProducer.send()发送消息,实际上只是把消息保存到RecordAccumulator中;
~~~ 后台线程KafkaThread扫描到RecordAccumulator中有消息后,将消息发送到kafka集群;
~~~ 如果发送成功,那么返回成功;
~~~ 如果发送失败,那么判断是否允许重试。如果不允许重试,那么返回失败的结果;
~~~ 如果允许重试,把消息再保存到RecordAccumulator中,等待后台线程KafkaThread扫描再次发送;
### ---可恢复异常说明

~~~ 异常是RetriableException类型或者TransactionManager允许重试;
~~~ RetriableException类继承关系如下:

|NO.Z.00080|——————————|BigDataEnd|——|Hadoop&kafka.V65|——|kafka.v65|消息重复场景|解决方案.v01|_解决方案_02

### ---记录顺序问题

~~~ 如果设置max.in.flight.requests.per.connection 大于1
~~~ 默认5,单个连接上发送的未确认请求的最大数量表示上一个发出的请求没有确认下一个请求又发出了。
~~~ 大于1可能会改变记录的顺序,因为如果将两个batch发送到单个分区,第一个batch处理失败并重试,
~~~ 但是第二个batch处理成功,那么第二个batch处理中的记录可能先出现被消费。
~~~ 设置max.in.flight.requests.per.connection 为1,可能会影响吞吐量,
~~~ 可以解决单个生产者发送顺序问题。
~~~ 如果多个生产者,生产者1先发送一个请求,生产者2后发送请求,此时生产者1返回可恢复异常,
~~~ 重试一定次数成功了。虽然生产者1先发送消息,但生产者2发送的消息会被先消费。

二、生产者发送重复解决方案

### ---生产者发送重复解决方案

~~~ # 启动kafka的幂等性
~~~ 要启动kafka的幂等性,设置: enable.idempotence=true ,以及ack=all 以及retries > 1 。
~~~ # ack=0,不重试。
~~~ 可能会丢消息,适用于吞吐量指标重要性高于数据丢失,例如:日志收集。

三、生产者和broke阶段消息丢失场景

### ---生产者和broke阶段消息丢失场景

~~~ # ack=0,不重试
~~~ 生产者发送消息完,不管结果了,如果发送失败也就丢失了。
~~~ # ack=1,leader crash
~~~ 生产者发送消息完,只等待Leader写入成功就返回了,Leader分区丢失了,
~~~ 此时Follower没来及同步,消息丢失。
~~~ # unclean.leader.election.enable 配置true
~~~ 允许选举ISR以外的副本作为leader,会导致数据丢失,默认为false。
~~~ 生产者发送异步消息,只等待Lead写入成功就返回,Leader分区丢失,
~~~ 此时ISR中没有Follower,Leader从OSR中选举,因为OSR中本来落后于Leader造成消息丢失。

四、解决生产者和broke阶段消息丢失

### ---禁用unclean选举,ack=all

~~~ ack=all / -1,tries > 1,unclean.leader.election.enable : false
~~~ 生产者发完消息,等待Follower同步完再返回,如果异常则重试。
~~~ 副本的数量可能影响吞吐量,不超过5个,一般三个。
~~~ 不允许unclean Leader选举。
### ---配置:min.insync.replicas > 1

~~~ 当生产者将acks 设置为all (或-1 )时, min.insync.replicas>1 。
~~~ 指定确认消息写成功需要的最小副本数量。
~~~ 达不到这个最小值,生产者将引发一个异常(要么是NotEnoughReplicas,
~~~ 要么是NotEnoughReplicasAfterAppend)。
~~~ 当一起使用时, min.insync.replicas 和ack 允许执行更大的持久性保证。
~~~ 一个典型的场景是创建一个复制因子为3的主题,设置min.insync复制到2个,用all 配置发送。
~~~ 将确保如果大多数副本没有收到写操作,则生产者将引发异常。
### ---失败的offset单独记录

~~~ 生产者发送消息,会自动重试,遇到不可恢复异常会抛出,
~~~ 这时可以捕获异常记录到数据库或缓存,进行单独处理。











Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart

                                                                                                                                                   ——W.S.Landor