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

相关推荐
霸道流氓气质1 小时前
分布式锁(Distributed Lock)详解
分布式
j7~2 小时前
【仿 RabbitMQ 项目】发布订阅消息队列 (篇九):队列数据管理,内存镜像 + SQLite 持久化实现
数据库·c++·分布式·sqlite·rabbitmq·队列数据管理
做一个AK梦2 小时前
分布式CAP理论
分布式
此时不提桶,更待何时15 小时前
06-23-A-RabbitMQ生态集成详解
分布式·rabbitmq
m0_6464299720 小时前
RabbitMQ 核心知识总结(生产实战版)
分布式·rabbitmq
DBA_G1 天前
GBase 8c分布式数据库客户端工具 gsql
数据库·分布式
chunyublog1 天前
Spark 词频统计练习
大数据·分布式·spark
ClouGence1 天前
开源数据库管理工具 CloudDM 4.4.0 发布,新增 Kafka、SAP HANA 数据源
数据库·kafka
acrel71 天前
高速公路分布式光伏并网电能质量与安全保护方案应用
分布式·安全