【Kafka】知识点(二)Kafka API的使用

一、生产者API:生产数据到Kafka

  • 路径

    • step1:构建ProducerRecord对象

    • step2:调用KafkaProducer的send方法将数据写入Kafka

  • 实施

    package bigdata.itcast.cn.kafka.producer;
    
    import org.apache.kafka.clients.producer.KafkaProducer;
    import org.apache.kafka.clients.producer.ProducerRecord;
    
    import java.util.Properties;
    
    /**
     * @ClassName KafkaProducerTestClient
     * @Description TODO 实现JavaAPI开发生产者客户端,实现生产数据到Kafka中
     * @Date 2021/11/27 11:20
     * @Create By     Frank
     */
    public class KafkaProducerTestClient {
        public static void main(String[] args) {
            // todo:1-构建连接
            // 构建一个配置对象
            Properties props = new Properties();
            //指定Kafka服务端地址
            props.put("bootstrap.servers", "node1:9092,node2:9092,node3:9092");
            /**
             *  Kafka怎么保证生产者写入的数据不丢失的?数据经过网络传递,必然会出现丢包等数据丢失的情况
             *  ACK :生产者写入一条数据到Kafka的分区中,Kafka会返回一个ack确认,生产者收到这个ack确认,再写入下一条数据
             *  Retry:如果超过一定时间,Kafka没有返回ack,就认为数据丢失,生产者会重新发送写入这条数据,如果依旧没有收到,就会重试一定次数
             *  0:生产者不用等待ack,发送完一条,直接发送下一条,最快,最不安全,肯定不用
             *  1:生产者写入数据到这个分区中,Kafka将数据写入这个分区的leader副本以后,就返回ack,生产者收到ack,再发送下一条,安全和性能折中方案
             *  all:生产者写入数据到这个分区中,Kafka等待这个分区的所有副本都同步了这条数据,再返回ack,生产者收到ack,再发送下一条,最安全,性能最差
             */
            props.put("acks", "all");
            //定义写入Kafka的KV序列化的类型
            props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
            props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
            // 构建一个连接对象,指定KV的类型,加载配置
            KafkaProducer<String, String> producer = new KafkaProducer<>(props);
    
            // todo:2-实现操作
            for (int i = 0; i < 10; i++){
                //调用连接对象方法将数据写入Kafka
                //ProducerRecord:每一条写入的数据必须封装为ProducerRecord对象
                //方式一:指定Topic、Key、Value
    //            producer.send(new ProducerRecord<String, String>("bigdata01", i+"", "itcast"+i));
                //方式二:指定Topic、Value
    //            producer.send(new ProducerRecord<String, String>("bigdata01",  "itcast"+i));
                //方式三:指定Topic、Partition、Key、Value
                producer.send(new ProducerRecord<String, String>("bigdata01",0,i+"","itcast"+i));
            }
    
            // todo:3-释放连接
            producer.close();
        }
    }

二、消费者API:消费Topic数据

  • 路径

    • step1:消费者订阅Topic

    • step2:调用poll方法从Kafka中拉取数据,获取返回值

    • step3:从返回值中输出:Topic、Partition、Offset、Key、Value

  • 实施

  • package bigdata.itcast.cn.kafka.consumer;
    ​
    import org.apache.kafka.clients.consumer.ConsumerRecord;
    import org.apache.kafka.clients.consumer.ConsumerRecords;
    import org.apache.kafka.clients.consumer.KafkaConsumer;
    ​
    import java.time.Duration;
    import java.util.Arrays;
    import java.util.Properties;
    ​
    /**
     * @ClassName KafkaConsumerTestClient
     * @Description TODO 实现JavaAPI开发Kafka消费者,消费Kafka中Topic中的数据
     * @Date 2021/11/27 11:47
     * @Create By     Frank
     */
    public class KafkaConsumerTestClient {
        public static void main(String[] args) {
            // todo:1-构建消费者连接
            Properties props = new Properties();
            // 指定服务端地址
            props.setProperty("bootstrap.servers", "node1:9092,node2:9092,node3:9092");
            // 指定这个消费者属于哪个消费者组,每个消费者组都会有1个id
            props.setProperty("group.id", "test01");
            // 开启自动提交
            props.setProperty("enable.auto.commit", "true");
            // 自动提交的时间间隔
            props.setProperty("auto.commit.interval.ms", "1000");
            // 消费者读取数据KV的反序列化类型
            props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
            props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
            // 构建一个消费者连接对象,指定KV类型以及加载配置
            KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
    ​
            // todo:2-订阅、消费、处理
            //订阅Topic
            consumer.subscribe(Arrays.asList("bigdata01"));
            //消费&处理
            while (true) {
                // 消费:从Kafka拉取数据,拉取到的所有数据放在ConsumerRecords集合对象中
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
                // 处理:取出每一条数据
                for (ConsumerRecord<String, String> record : records){
                    String topic = record.topic();//这条数据属于哪个topic
                    int part = record.partition();//这条数据属于这个Topic的哪个分区
                    long offset = record.offset();//这条数据在这个分区中的offset是什么
                    String key = record.key();//获取数据中的Key
                    String value = record.value();//获取数据中的Value
                    //输出
                    System.out.println(topic+"\t"+part+"\t"+offset+"\t"+key+"\t"+value);
                }
    ​
            }
    ​
            // todo:3-实时计算中:消费者是不停的
        }
    }

三、消费者消费过程及问题

  • 路径

    • step1:消费者是如何消费Topic中的数据的?

    • step2:如果消费者故障重启,消费者怎么知道自己上次消费的位置的?

  • 实施

    • Kafka中消费者消费数据的规则

      • 消费者消费Kafka中的Topic根据每个分区的Offset进行消费,每次从上一次的位置继续消费

      • 第一次消费规则:由属性决定

        auto.offset.reset = latest | earliest | none
        latest:默认的值,从Topic每个分区的最新的位置开始消费
        earliest:从最早的位置开始消费,每个分区的offset为0开始消费
        none:如果是第一次消费,这个属性为none,Kafka会抛出异常
        如果不是第一次消费:上面这个属性不起作用
      • 第二次消费开始:根据上一次消费的Offset位置+1继续进行消费

        • 假如上一次消费到的offset=3,那么第二次就该消费offset+1的数据了。

      • 问题1:消费者如何知道上一次消费的位置是什么?

        • 消费者每次成功消费会在自己的内存中记录offset的值,下次直接请求上一次消费的位置

        • Consumer Offset:表示当前消费者组已经消费到的位置

        • Commit Offset:表示下一次要消费的位置,等于Consumer Offset+1

      • 问题2

        • 只有1个消费:如果因为一些原因,消费者故障了,重启消费者,相当于一个新的消费者,原来内存中offset就没有了,消费者怎么知道上一次消费的位置?

        • 如果多个消费者:因为一些原因,某个消费者故障,这个消费者本来负责的分区,Kafka会交给别的消费者来继续消费,这个接盘侠它怎么知道之前的那个消费者消费到的位置?

        • 如果不知道上一次的位置,就无法接着上次的位置继续消费

          • 要么重头消费:数据重复

          • 要么最新消费:数据丢失

        • 原因:offset只放在内存中,进程故障,内存的数据丢失,Offset也就丢失了

        • 解决:将offset持久化存储并且共享

    • Kafka Offset偏移量管理

      • Kafka将每个消费者消费的Commit Offset主动记录在一个Topic中:__consumer_offsets

      • 如果下次消费者没有给定请求offset,kafka就根据自己记录的offset来提供消费的位置

    • 提交的规则:根据时间自动提交

      props.setProperty("enable.auto.commit", "true");//是否自动提交offset
      props.setProperty("auto.commit.interval.ms", "1000");//提交的间隔时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值