kafka之重复消费数据

在进入主题之前,我们先思考一个问题。
问题

kafka消费者使用自动提交的模式,提交间隔为2s,消费者在获取数据的时候处理0.5s,从kafka拉取过来的数据只够处理1秒。那么消费者下次拉取过来的数据是否是已经消费完的数据?或者说由于数据已经消费,但是偏移量没有被提交,是否会造成下次获取的数据是从旧的偏移量开始拉取?


答案

不会是旧数据,kafka的消费者也有自己偏移量,这个偏移量是从kafka中读取的量,和kafka提交的偏移量不一样。假设变成自动提交偏移量,而且没有写提交的逻辑,同一个消费者,除了第一次或者rebalance会根据已提交的offset来获取数据,剩下的时候都是根据自己本地的偏移量来获取的。这个模式有点类似于用桶取水,用瓢来喝水。消费者就是桶的角色,poll就是瓢的角色。

重复消费的情况

我们把重复消费的情况分为2种,一种是想避免的,一种是故意如此的。

想避免的场景

  1. 消费者使用了自动提交模式,当还没有提交的时候,有新的消费者加入或者移除,发生了rebalance。再次消费的时候,消费者会根据提交的偏移量来,于是重复消费了数据。
  2. 使用异步提交,并且在callback里写了失败重试,但是没有注意顺序。例如提交5的时候,发送网络故障,由于是异步,程序继续运行,再次提交10的时候,提交成功,此时正好运行到5的重试,并且成功。当发生了rebalance,又会重复消费了数据。

注:这里不讨论那个消费者提交的offset的作用。

故意的场景

  1. 使用不同的组消费同一个topic。改个 group.id属性即可。
  2. 自己手动提交偏移量。 这里的麻烦的地方就是需要理解开头的问题,并不是说你提交完就可以了。你得想个办法去读取那个偏移量再次消费。下面提供一个暴力的手段,关闭消费者,然后再次开启新的。
 public static void consumer(Properties properties,String info) {
        System.out.println(info);
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer(properties);
        kafkaConsumer.subscribe(Arrays.asList(new String[]{"hello"}));
        boolean flag = false;
        while (true) {
            ConsumerRecords<String, String> poll = kafkaConsumer.poll(100);
            if (!poll.isEmpty()) {
                for (ConsumerRecord<String, String> o : poll) {
                    System.out.println(o.value() + o.offset());
                    //假设场景为重复消费3,这里需要根据业务来提交便宜量
                    if (o.offset() == 3) {
                        //手动提交偏移量
                        Map<TopicPartition, OffsetAndMetadata> currentOffset = new HashMap<TopicPartition, OffsetAndMetadata>();
                        //提交的偏移量,这个偏移量就是下次消费的第一条数据
                        currentOffset.put(new TopicPartition(o.topic(), o.partition()), new OffsetAndMetadata(o.offset()+1, ""));
                        kafkaConsumer.commitSync(currentOffset);
                        flag = true;
                        break;
                    }
                }
            }
            if(flag){
                kafkaConsumer.close();
                break;
            }
        }
    }

这里也必须注意,kafka并不是数据库,他保存的数据有持久化的时间和大小的限制,可能你提交的偏移量的数据已经被kafka清理掉了。

### Kafka 重复消费数据的原因 Kafka 中的重复消费问题主要由以下几个方面引起: 1. **Offset 提交失败** 当消费者读取并处理了一条消息后,在 Offset 尚未成功提交的情况下,如果发生异常(如程序崩溃、网络中断等),消费者重启后会从之前的 Offset 处重新开始消费[^5]。 2. **Session Timeout 超时引发 Rebalance** 如果消费者的处理逻辑较为耗时,导致其未能及时向 Kafka 发送心跳信号,则可能超过 `session.timeout.ms` 配置的时间限制。这种情况下,Kafka 认为该消费者已离线,并触发 Rebalance 过程。Rebalance 后,由于 Offset 可能尚未提交消费者会从旧位置继续消费,从而导致重复消费[^2]。 3. **Max Poll Interval 超时** 若单次 poll 拉取消息后的处理时间超过了配置参数 `max.poll.interval.ms` 的值,默认为 5 分钟,也会使 Kafka 判断当前消费者无法正常工作而将其移除出 Consumer Group 并启动 Rebalance 流程。同样地,这可能导致 Offset 提交失败进而产生重复消费现象[^4]。 4. **Consumer Partition 重分配** 在某些特殊场景下,例如动态调整分区数量或者手动干预 Consumer Group 成员构成时,可能会触发 Partition 的重新分配操作。在此期间如果没有妥善管理好 Offset 状态信息,则容易出现重复消费情况[^3]。 --- ### 解决方案 针对以上提到的各种原因,可以采取以下几种方式来减少甚至完全消除 Kafka 数据重复消费可能性: #### 方法一:合理设置超时参数 - 增大 `session.timeout.ms` 和 `max.poll.interval.ms` 参数值以适应更复杂的业务需求,给予足够的缓冲时间让每批次的消息能够顺利完成处理后再进行后续动作。 ```properties session.timeout.ms=45000 max.poll.interval.ms=900000 ``` #### 方法二:采用精确一次性语义 (Exactly Once Semantics, EOS) - 使用事务机制配合幂等生产者功能实现 Exactly Once Delivery 效果。这种方式能够在一定程度上保障即使发生了意外状况也不会丢失任何一条记录同时也杜绝了多余副本的存在可能性[^1]。 #### 方法三:优化应用性能降低延迟风险 - 改善应用程序内部结构设计提高效率缩短每次请求所需耗费的实际秒数;另外还可以考虑引入多线程异步执行模式进一步加快整体流程运转速度防止因个别环节卡顿影响全局稳定性表现。 #### 方法四:自定义偏移量控制策略 - 不依赖于框架自带自动提交机制而是改用手动方式进行精细化调控确保只有当确认无误之后才会正式保存对应的位置索引值至远程存储介质之中去避免潜在隐患的发生几率增加系统可靠性水平。 --- ### 总结说明 综上所述,通过对 Kafka 客户端相关配置项做出适当修改以及运用先进的技术手段相结合的办法可以从根源上去遏制住那些不必要的冗余再现行为达到预期目标效果即有效预防和缓解 Kafaka 平台之上所面临的数据一致性挑战难题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值