kafka重复消费

目录

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:对于每个 ProducerID,Producer 发送数据的每个 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 writes)

Kafka事务的回滚,并不是删除已写入的数据,而是将写入数据的事务标记为 Rollback/Abort 从而在读数据时过滤该数据。

java 复制代码
// @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、基于数据库的唯一索引, 新增的数据在数据库中存在重复的直接报错,不会出现脏数据

相关推荐
8Qi812 小时前
DDD架构演进
java·分布式·mvc·ddd
方方洛12 小时前
ray教程-23-附录C-术语表
人工智能·分布式·机器学习
皮皮林55113 小时前
Kafka 怎么保证消费只消费一次的?
kafka
茶海棠18 小时前
Hadoop+zookeeper+HBase+Kafka集群搭建全流程
hadoop·zookeeper·kafka·hbase
海棠Flower未眠20 小时前
下单请求处理流程:商品维度分布式锁与 Redis 预扣库存
redis·分布式·junit
罗四强21 小时前
第10篇:打包上架:签名、打包 HAP、发布到华为应用市场
分布式·操作系统·harmonyos·arkts·鸿蒙系统
秋饼21 小时前
Redis 全面解析:从数据结构到主从/哨兵/集群、分布式锁与面试高频考点,代码到原理一次讲透
数据库·redis·分布式·面试·后端开发
二月夜21 小时前
Redis 高频面试题全解:缓存三大问题、大 key 治理与分布式锁之争(2026 招聘 JD 真题复盘)
redis·分布式·后端·缓存
后台模板学习1 天前
Kafka 与 RabbitMQ 在用户认证与权限管理场景
kafka·rabbitmq·linq
樱花落木兰1 天前
Redis 消息队列的可靠性演进:从 List、Pub/Sub 到 Stream 消费者组,以及 8.x 版本改变了什么
java·开发语言·经验分享·redis·分布式·python·软件工程