
目录1、生产者重复发送消息生产者幂等性生产者事务消息2、消费者重复消费消息导致重复消费的情况1、生产者重复发送消息2、消费者重复消费消息1、生产者重复发送消息生产者发送了消息没有收到broker正确的应答响应导致生产者重试。生产者发出一条消息broker落盘后因为网络原因发送端得到一个发送失败的响应或超时网络中断然后生产者收到一个可恢复的Exception重试发送消息导致重复。重试过程1、new KafkaProducer() 后创建一个后台线程 KafkaThread 负责发送消息到Kafka集群2、调用 KafkaProducer.send() 发送消息实际上只是把消息保存到 RecordAccumulator 中3、RecordAccumulator 某一个批次被填满或者等待时间超过了所设定的阈值的时候此时KafkaThread 线程就开始拉取消息发送消息到Kafka集群4、如果发送成功那么返回成功5、如果发送失败那么判断是否允许重试。如果不允许重试那么返回失败的结果如果允许重试把消息再保存到 RecordAccumulator 中等待后台线程 KafkaThread 再次发送。在Kafka中消息存在三种语义1、最多一次At most once消息可能会丢失但绝不会被重复发送 ---- Producer禁止重试2、至少一次At least once消息不会丢失但有可能被重复发送 ---- Producer开启了重试机制当发送的消息没有收到Kafka集群成功的应答时会再次发送数据3、仅有一次Exactly once消息不会丢失也不会被重复发送 ---- Kafka通过幂等性和事务这两个机制保证了精准一次生产者幂等性kafka为了实现幂等性在 0.11.0 版本之后它在底层设计架构中引入了ProducerID和SequenceNumber。1、ProducerID在每个新的 Producer 初始化时会被分配一个唯一的 ProducerID这个 ProducerID 对客户端使用者是不可见的。2、SequenceNumber对于每个 ProducerIDProducer发送数据的每个 Topic 和 Partition 都对应一个从 0 开始单调递增的 SequenceNumber 值。异常情况如下图所示当 Producer 发送消息 (x2,y2) 给 Broker 时Broker 接收到消息并将其追加到消息流中。此时Broker 返回 Ack 信号给 Producer 时发生异常导致Producer 接收 Ack 信号失败。对于 Producer 来说会触发重试机制将消息 (x2,y2) 再次发送但是由于引入了幂等性在每条消息中附带 PID(ProducerID) 和 equenceNumber。相同的 PID 和 SequenceNumber 发送给 Broker而之前 Broker 缓存过之前发送的相同的消息那么在消息流中的消息就只有一条 (x2,y2)不会出现重复发送的情况。注意幂等生产者只能保证单个主题的单一分区内消息不重复其次它只能实现单会话上的幂等性不能实现跨会话的幂等性这里的会话即可以理解为Producer 进程的一次运行。当重启了 Producer 进程之后则幂等性保证就失效了。如果我想实现多分区以及多会话上的消息无重复应该怎么做呢答案就是事务生产者事务消息Kafka的事务特性本质上是支持了Kafka跨分区和Topic的原子写操作。通过事务机制KAFKA 可以实现对多个 topic 的多个 partition 的原子性的写入即处于同一个事务内的所有消息不管最终需要落地到哪个 topic 的哪个 partition, 最终结果都是要么全部写成功要么全部写失败Atomic multi-partition writesKafka事务的回滚并不是删除已写入的数据而是将写入数据的事务标记为 Rollback/Abort 从而在读数据时过滤该数据。// Transactional // 方式一需要添加该注解但是添加该注解事务会自动回滚 Test public void sendMsgTransaction() throws ExecutionException, InterruptedException { // 方式一 /*kafkaTemplate.send(topic-02 , 0 , null , msg11....) ; int a 1 / 0 ; kafkaTemplate.send(topic-02 , 1 , null , msg12....) ;*/ // 方式二 kafkaTemplate.executeInTransaction(operations - { operations.send(topic-02 , 0 , null , msg09....) ; int a 1 / 0 ; operations.send(topic-02 , 1 , null , msg10....) ; return true ; }) ; }2、消费者重复消费消息# 解决方案1、消费端手动移位提交2、消费端做幂等处理# 幂等操作1、新增时先查一下如果有了就不操作了2、更新需要set为某个值不需要处理天然幂等3、生产者每次传递一个唯一id消费消息时先去redis中查一下是否已经消费如果没有就消费消息然后把id存入到redis中如果存在就不用处理了4、基于数据库的唯一索引 新增的数据在数据库中存在重复的直接报错不会出现脏数据