Kafka API的运用(Consumer API)

四、Kafka API

2、 Consumer API

  • Consumer 消费数据时的可靠性是很容易保证的,因为数据在 Kafka 中是持久化的,故不用担心数据丢失问题
  • 由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费
  • 所以offset的维护是 Consumer 消费数据是必须考虑的问题。
2.1 自动提交offset

1)编写代码

  • 需要用到的类:
    • KafkaConsumer:需要创建一个消费者对象,用来消费数据
    • ConsumerConfig:获取所需的一系列配置参数
    • ConsuemrRecord每条数据都要封装成一个 ConsumerRecord 对象
  • 为了使我们能够专注于自己的业务逻辑,Kafka 提供了自动提交offset的功能
  • 自动提交 offset 的相关参数
    • enable.auto.commit:是否开启自动提交offset功能
    • auto.commit.interval.ms:自动提交offset的时间间隔

2)消费者自动提交offset

  • 编写 MyConsumer
public class MyConsumer {
    public static void main(String[] args) {
        //todo 创建一个配置对象
        Properties props = new Properties();
        //1.去配置集群
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop105:9092");
        //2.指定消费者的组
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "bajie");
        //3.指定自动提交偏移量
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
        //4.每次自动提交偏移量的间隔时间
        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
        //5.设定key和value的反序列化
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

        //todo 创建一个消费者对象
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        //对于消费者需要订阅主题
        ArrayList<String> topics = new ArrayList<>();
        //topics.add("wukong");
        topics.add("bajie");
        consumer.subscribe(topics);
        while (true){
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
            for (ConsumerRecord<String, String> record : records) {
                System.out.println("topic:"+record.topic()+
                        "--partition:"+record.partition()+
                        "--offset:"+record.offset()+
                        "--key:"+record.key()+
                        "--value:"+record.value());
            }
        }
    }
}

启动消费者,再启动生产者

生产者:
topic:bajie--partition:0--offset:44
topic:bajie--partition:0--offset:45
topic:bajie--partition:0--offset:46
topic:bajie--partition:0--offset:47
topic:bajie--partition:0--offset:48
topic:bajie--partition:0--offset:49
topic:bajie--partition:0--offset:50
topic:bajie--partition:0--offset:51
topic:bajie--partition:0--offset:52
topic:bajie--partition:0--offset:53

消费者:
topic:bajie--partition:0--offset:44--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---0
topic:bajie--partition:0--offset:45--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---1
topic:bajie--partition:0--offset:46--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---2
topic:bajie--partition:0--offset:47--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---3
topic:bajie--partition:0--offset:48--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---4
topic:bajie--partition:0--offset:49--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---5
topic:bajie--partition:0--offset:50--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---6
topic:bajie--partition:0--offset:51--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---7
topic:bajie--partition:0--offset:52--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---8
topic:bajie--partition:0--offset:53--key:null--value:er shi xiong shi fu bei yao guai zhua zou le---9

注意:关闭消费者后,生产生产数据后,再启动消费者,消费者可以消费到数据(偏移量)启动消费者后先会看看是否有偏移量存在,有就可以消费

2.2 重置Offset
  • 使用重置offset的前提
    • 当Kafka中没有初始偏移量或者服务器上的当前偏移量不再存在
auto.offset.rest = earliest | latest | none |

在这里插入图片描述

编写ResetOffsetConsumer

public class ResetOffsetConsumer {
    public static void main(String[] args) {
        //todo 创建一个配置对象
        Properties props = new Properties();
        //1.去配置集群
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "hadoop105:9092");
        //2.指定消费者的组
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "test02");
        /**
         * auto.offset.reset: What to do when there is no initial offset in
         * Kafka or if the current offset does not exist any more on the server
         * (e.g. because that data has been deleted):
         * earliest: automatically reset the offset to the earliest offset
         * latest: automatically reset the offset to the latest offset
         * 当你想要重头开始消费 你需要满足3个条件
         * 1.你当前的消费者的消费者组从来没有消费过订阅的主题
         * 2.你当前的消费者的消费者组的offset已经找不到(过期了超过7天对应数据被删除了)
         * 3.你需要将AUTO_OFFSET_RESET_CONFIG置位earliest
         */
        //3.重置offset
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        //4.指定自动提交偏移量
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
        //5.每次自动提交偏移量的间隔时间
        props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
        //6.设定key和value的反序列化
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        //todo 创建一个消费者对象
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        //对于消费者需要订阅主题
        ArrayList<String> topics = new ArrayList<>();
        topics.add("bajie");
        topics.add("wukong");
        consumer.subscribe(topics);
        while (true){
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
            for (ConsumerRecord<String, String> record : records) {
                System.out.println("topic:"+record.topic()+
                        "--partition:"+record.partition()+
                        "--offset:"+record.offset()+
                        "--key:"+record.key()+
                        "--value:"+record.value());
            }
        }
    }
}
  • 执行结果和–from-beginning 一样,能消费到之前所有的数据
2.3手动提交 offset
  • 虽然自动提交offset十分简介便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因此 Kafka还提供了手动提交offset的API
  • 手动提交offset的方法有两种:分别是commitSync(同步提交)commitAsync(异步提交)。两者的相同点是,都会将本次poll的一批数据最高的偏移量提交不同点是,commitSync 阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而 commitAsync则没有失败重试机制,故有可能提交失败。

1)同步提交 offset

  • 由于同步提交 offset 有失败重试机制,故更加可靠,以下为同步提交offset的示例。
public class CustomComsumer {

    public static void main(String[] args) {

        Properties props = new Properties();

		//Kafka集群
        props.put("bootstrap.servers", "hadoop105:9092"); 

		//消费者组,只要group.id相同,就属于同一个消费者组
        props.put("group.id", "test"); 

        props.put("enable.auto.commit", "false");//关闭自动提交offset

        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

        consumer.subscribe(Arrays.asList("bajie"));//消费者订阅主题

        while (true) {

			//消费者拉取数据
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(1));
            for (ConsumerRecord<String, String> record : records) {
                System.out.println(record.topic()+" -- "+
                                   record.partition()+" -- "+
                                   record.offset()+" -- "+
                                   record.key()+" -- "+
                                   record.value());
            }
			//同步提交,当前线程会阻塞直到offset提交成功
            consumer.commitSync();
        }
    }
}

2)异步提交offset

  • 虽然同步提交offset更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞吐量会收到很大的影响。因此更多的情况下,会选用异步提交offset的方式。

以下为异步提交offset的示例:

public class CustomConsumer {

    public static void main(String[] args) {

        Properties props = new Properties();

        //Kafka集群
        props.put("bootstrap.servers", "hadoop105:9092"); 

        //消费者组,只要group.id相同,就属于同一个消费者组
        props.put("group.id", "test"); 

        //关闭自动提交offset
        props.put("enable.auto.commit", "false");

        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList("bajie"));//消费者订阅主题

        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);//消费者拉取数据
            for (ConsumerRecord<String, String> record : records) {
                      System.out.println(record.topic()+" -- "+
                                   record.partition()+" -- "+
                                   record.offset()+" -- "+
                                   record.key()+" -- "+
                                   record.value());          
                          }  

//异步提交
            consumer.commitAsync(new OffsetCommitCallback() {
                @Override
                public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
                    if (exception != null) {
                        System.err.println("Commit failed for" + offsets);
                    }
                }
            }); 
        }
    }
}

3) 数据漏消费和重复消费分析

  • 无论是同步提交还是异步提交offset,都有可能会造成数据的漏消费或者重复消费先提交offset后消费,有可能造成数据的漏消费;而先消费后提交offset,有可能会造成数据的重复消费。

注意:消费者偏移量存储是记录下一次从哪消费,大小是当前消费数加1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值