kafka消费者提交偏移量的方式对比同步、自动、异步、组合提交等、基本代码实现、以及他们导致的问题

本文详细介绍了Kafka消费者如何通过自动提交、手动sync/async提交来管理消息消费偏移量,包括重复消费与丢失消费的图解,以及在不同场景下的组合使用技巧,重点讲解特殊提交方法和避免消息重复的策略。
摘要由CSDN通过智能技术生成

目录

 

1 基本概念

2 提交方式

2.0 重复消费和丢失消费图解

2.1 自动提交

2.2 手动提交(同步提交)commitsync

2.4 异步提交commitAsync

2.3 同步和异步组合提交commitAsync()和 commitsync()

2.4 特殊提交、未消费完一个批次消息时提交偏移量


注:基本代码和信息来自享学课堂,自学略有修改

1 基本概念

  • 提交:消费者消费完消息之后,更新自己消费那个消息的操作
  • _consumer_offset:消费者消费完消息之后,会往_consumer_offset主题发送消息,_consumer_offset保存每个分区的偏移量
  • 分区再均衡:消费者的数量发生变化,或者主题分区数量发生变化,会修改消费者对应的分区关系,叫做分区再均衡:保证kafka高可用和伸缩性;缺点:在均衡期间,消费者无法读取消息,群组短时间不可用
 

2 提交方式

2.0 重复消费和丢失消费图解

重复消费图解:

丢失消费图解:

2.1 自动提交

自动提交是最贱的提交偏移量方式, 消费者会自动把从 poll() 方法接收到的 最大 偏移量提交上去。
参数:
  • enable.auto.comnit=true:在消费者close()的时候也会自动提交
  • auto.commit.interval.ms=默认5s,没过5秒就会提交偏移量,但是在4秒发生了分区在均衡,偏移量还没来得及提交,他们这四秒的消息就会被重复消费
问题:
 
自动提交虽然方便 , 但是很明显是一种基于时间提交的方式 , 不过并没有为我们留有余地来避免重复处理消息。

2.2 手动提交(同步提交)commitsync

参数:

auto.commit. offset = false:使用commitsync()提交poll()返回最新偏移量

注意:
  1. 处理完业务之后,一定要手动调用commitsync()
  2. 如果发生了在均衡,由于当前commitsync偏移量还未提交,所以消息会被重复消费
  3. commitsync会阻塞直到提交成功
 
 
代码示例:
public class CommitSync {

    public static void main(String[] args) {
        /**消息消费者*/
        Properties properties = new Properties();
        properties.put("bootstrap.servers","127.0.0.1:9092");
        properties.put("key.deserializer", StringDeserializer.class);
        properties.put("value.deserializer", StringDeserializer.class);
        /*取消自动提交*/
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);

        KafkaConsumer<String,String> consumer = new KafkaConsumer<String, String>(properties);
        try {
            consumer.subscribe(Collections.singletonList("simple"));
            while(true){
                ConsumerRecords<String, String> records = consumer.poll(500);
                for(ConsumerRecord<String, String> record:records){
                    System.out.println(String.format(
                            "主题:%s,分区:%d,偏移量:%d,key:%s,value:%s",
                            record.topic(),record.partition(),record.offset(), record.key(),record.value()));
                    //自定义业务逻辑
                }
                //开始事务
                //读业务写数据库-
                //偏移量写入数据库
                //提交
                consumer.commitSync();
            }
        } finally {
            consumer.close();
        }
    }
}

2.4 异步提交commitAsync

 
注意:
  1. commitAsync()不会重试提交偏移量,重试提交可能会导致重复消费
  2. commitAsync()也支持回调,broker 作出响应时会执行回调。回调经常被用于记录提交错误或生成度量指标。
public class CommitAsync {

    public static void main(String[] args) {
        /**消息消费者*/
        Properties properties = new Properties();
        properties.put("bootstrap.servers","127.0.0.1:9092");
        properties.put("key.deserializer", StringDeserializer.class);
        properties.put("value.deserializer", StringDeserializer.class);
        /**取消自动提交*/
        properties.put("enable.auto.commit",false);

        KafkaConsumer<String,String> consumer = new KafkaConsumer<String, String>(properties);
        try {
            consumer.subscribe(Collections.singletonList("simple"));
            while(true){
                ConsumerRecords<String, String> records = consumer.poll(500);
                for(ConsumerRecord<String, String> record:records){
                    System.out.println(String.format("主题:%s,分区:%d,偏移量:%d,key:%s,value:%s",
                            record.topic(),record.partition(),record.offset(), record.key(),record.value()));
                    //自定义业务逻辑开发
                }
                //消费完毕,同步提交
                consumer.commitAsync();
                /**允许执行回调*/
//                consumer.commitAsync(new OffsetCommitCallback() {
//                    @Override
//                    public void onComplete( Map<TopicPartition, OffsetAndMetadata> offsets,Exception exception) {
//                        if(exception!=null){
//                            System.out.print("Commmit failed for offsets ");
//                            System.out.println(offsets);
//                            exception.printStackTrace();
//                        }
//                    }
//                });
            }
        } finally {
            consumer.close();
        }
    }
}

 

2.3 同步和异步组合提交commitAsync()commitsync()

理由:
中间处理消息的时候,即使偶尔出现一次偏移量提交失败,后面消费的时候,偏移量也能够提交成功,所以不会有大影响;但是到了最后消费者要关闭了的时候,偏移量一定要提交成功。 因此, 在消费者关闭前一般会组合使用 commitAsync() commitsync()

同步一定会提交成功,异步可能会失败

代码示例:

public class SyncAndAsync {
    public static void main(String[] args) {
        /**消息消费者*/
        Properties properties = new Properties();
        properties.put("bootstrap.servers","127.0.0.1:9092");
        properties.put("key.deserializer", StringDeserializer.class);
        properties.put("value.deserializer", StringDeserializer.class);
        /*取消自动提交*/
        properties.put("enable.auto.commit",false);

        KafkaConsumer<String,String> consumer = new KafkaConsumer<String, String>(properties);
        try {
            consumer.subscribe(Collections.singletonList("simple"));
            while(true){
                ConsumerRecords<String, String> records = consumer.poll(500);
                for(ConsumerRecord<String, String> record:records){
                    System.out.println(String.format(
                            "主题:%s,分区:%d,偏移量:%d,key:%s,value:%s",
                            record.topic(),record.partition(),record.offset(),
                            record.key(),record.value()));
                    //自定义工作
                }
                //避免等待,异步提交
                consumer.commitAsync();
            }
        } catch (CommitFailedException e) {
            System.out.println("提交失败");
            e.printStackTrace();
        } finally {
            try {
                //最后一次提交,确保成功,同步提交
                consumer.commitSync();
            } finally {
                consumer.close();
            }
        }
    }
}

2.4 特殊提交、未消费完一个批次消息时提交偏移量

注意:
  1. commitSync()commitAsync()来实现,因为它只会提交最后一个偏移量;
  2. 消费者 API 允许在调用 commitsync()commitAsync()方法时传进去希望提交的主题分区和偏移量的 map,就能够在一个批次消息未消费完时提交偏移量
 
代码示例:
 
 
public class CommitSpecial {
    public static void main(String[] args) {
        /**消息消费者*/
        Properties properties = new Properties();
        properties.put("bootstrap.servers","127.0.0.1:9092");
        properties.put("key.deserializer", StringDeserializer.class);
        properties.put("value.deserializer", StringDeserializer.class);
        /*取消自动提交*/
        properties.put("enable.auto.commit",false);

        KafkaConsumer<String,String> consumer = new KafkaConsumer<String, String>(properties);
        //主题分区-偏移量的关系
        Map<TopicPartition, OffsetAndMetadata> currOffsets = new HashMap<TopicPartition, OffsetAndMetadata>();
        int count = 0;
        try {
            consumer.subscribe(Collections.singletonList("simple"));
            while(true){
                ConsumerRecords<String, String> records = consumer.poll(500);
                for(ConsumerRecord<String, String> record:records){
                    System.out.println(String.format(
                            "主题:%s,分区:%d,偏移量:%d,key:%s,value:%s",
                            record.topic(),record.partition(),record.offset(),
                            record.key(),record.value()));
                    //保存每个主题分区和偏移量的关系,并不是在处理完毕消息列表才提交
                    currOffsets.put(new TopicPartition(record.topic(),record.partition()),
                            new OffsetAndMetadata(record.offset()+1,"no meta"));
                    if(count%11==0){
                        consumer.commitAsync(currOffsets,null);
                    }
                    count++;
                }
            }
        } finally {
            consumer.close();
        }
    }
}

 

 
 
 
 
 
 
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值