你真的了解Flink Kafka source吗?

Flink 提供了专门的 Kafka 连接器,向 Kafka topic 中读取或者写入数据。Flink Kafka Consumer 集成了 Flink 的 Checkpoint 机制,可提供 exactly-once 的处理语义。为此,Flink 并不完全依赖于跟踪 Kafka 消费组的偏移量,而是在内部跟踪和检查偏移量。
在这里插入图片描述

引言

当我们在使用Spark Streaming、Flink等计算框架进行数据实时处理时,使用Kafka作为一款发布与订阅的消息系统成为了标配。Spark Streaming与Flink都提供了相对应的Kafka Consumer,使用起来非常的方便,只需要设置一下Kafka的参数,然后添加kafka的source就万事大吉了。如果你真的觉得事情就是如此的so easy,感觉妈妈再也不用担心你的学习了,那就真的是too young too simple sometimes naive了。本文以Flink 的Kafka Source为讨论对象,首先从基本的使用入手,然后深入源码逐一剖析,一并为你拨开Flink Kafka connector的神秘面纱。值得注意的是,本文假定读者具备了Kafka的相关知识,关于Kafka的相关细节问题,不在本文的讨论范围之内。

Flink Kafka Consumer介绍

Flink Kafka Connector有很多个版本,可以根据你的kafka和Flink的版本选择相应的包(maven artifact id)和类名。本文所涉及的Flink版本为1.10,Kafka的版本为2.3.4。Flink所提供的Maven依赖于类名如下表所示:

Maven 依赖 自从哪个版本 开始支持 类名 Kafka 版本 注意
flink-connector-kafka-0.8_2.11 1.0.0 FlinkKafkaConsumer08 FlinkKafkaProducer08 0.8.x 这个连接器在内部使用 Kafka 的 SimpleConsumer API。偏移量由 Flink 提交给 ZK。
flink-connector-kafka-0.9_2.11 1.0.0 FlinkKafkaConsumer09 FlinkKafkaProducer09 0.9.x 这个连接器使用新的 Kafka Consumer API
flink-connector-kafka-0.10_2.11 1.2.0 FlinkKafkaConsumer010 FlinkKafkaProducer010 0.10.x 这个连接器支持 带有时间戳的 Kafka 消息,用于生产和消费。
flink-connector-kafka-0.11_2.11 1.4.0 FlinkKafkaConsumer011 FlinkKafkaProducer011 >= 0.11.x Kafka 从 0.11.x 版本开始不支持 Scala 2.10。此连接器支持了 Kafka 事务性的消息传递来为生产者提供 Exactly once 语义。
flink-connector-kafka_2.11 1.7.0 FlinkKafkaConsumer FlinkKafkaProducer >= 1.0.0 这个通用的 Kafka 连接器尽力与 Kafka client 的最新版本保持同步。该连接器使用的 Kafka client 版本可能会在 Flink 版本之间发生变化。从 Flink 1.9 版本开始,它使用 Kafka 2.2.0 client。当前 Kafka 客户端向后兼容 0.10.0 或更高版本的 Kafka broker。 但是对于 Kafka 0.11.x 和 0.10.x 版本,我们建议你分别使用专用的 flink-connector-kafka-0.11_2.11 和 flink-connector-kafka-0.10_2.11 连接器。

Demo示例

添加Maven依赖

<!--本文使用的是通用型的connector-->
<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-connector-kafka_2.11</artifactId>
  <version>1.10.0</version>
</dependency>

简单代码案例

public class KafkaConnector {
   

    public static void main(String[] args) throws Exception {
   

        StreamExecutionEnvironment senv = StreamExecutionEnvironment.getExecutionEnvironment();
        // 开启checkpoint,时间间隔为毫秒
        senv.enableCheckpointing(5000L);
        // 选择状态后端
        senv.setStateBackend((StateBackend) new FsStateBackend("file:///E://checkpoint"));
        //senv.setStateBackend((StateBackend) new FsStateBackend("hdfs://kms-1:8020/checkpoint"));
        Properties props = new Properties();
        // kafka broker地址
        props.put("bootstrap.servers", "kms-2:9092,kms-3:9092,kms-4:9092");
        // 仅kafka0.8版本需要配置
        props.put("zookeeper.connect", "kms-2:2181,kms-3:2181,kms-4:2181");
        // 消费者组
        props.put("group.id", "test");
        // 自动偏移量提交
        props.put("enable.auto.commit", true);
        // 偏移量提交的时间间隔,毫秒
        props.put("auto.commit.interval.ms", 5000);
        // kafka 消息的key序列化器
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        // kafka 消息的value序列化器
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        // 指定kafka的消费者从哪里开始消费数据
        // 共有三种方式,
        // #earliest
        // 当各分区下有已提交的offset时,从提交的offset开始消费;
        // 无提交的offset时,从头开始消费
        // #latest
        // 当各分区下有已提交的offset时,从提交的offset开始消费;
        // 无提交的offset时,消费新产生的该分区下的数据
        // #none
        // topic各分区都存在已提交的offset时,
        // 从offset后开始消费;
        // 只要有一个分区不存在已提交的offset,则抛出异常
        props.put("auto.offset.reset", "latest");
        FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>(
                "qfbap_ods.code_city",
                new SimpleStringSchema(),
                props);
        //设置checkpoint后在提交offset,即oncheckpoint模式
        // 该值默认为true,
        consumer.setCommitOffsetsOnCheckpoints(true);
     
        // 最早的数据开始消费
        // 该模式下,Kafka 中的 committed offset 将被忽略,不会用作起始位置。
        //consumer.setStartFromEarliest();

        // 消费者组最近一次提交的偏移量,默认。
        // 如果找不到分区的偏移量,那么将会使用配置中的 auto.offset.reset 设置
        //consumer.setStartFromGroupOffsets();

        // 最新的数据开始消费
        // 该模式下,Kafka 中的 committed offset 将被忽略,不会用作起始位置。
        //consumer.setStartFromLatest();

        // 指定具体的偏移量时间戳,毫秒
        // 对于每个分区,其时间戳大于或等于指定时间戳的记录将用作起始位置。
        // 如果一个分区的最新记录早于指定的时间戳,则只从最新记录读取该分区数据。
        // 在这种模式下,Kafka 中的已提交 offset 将被忽略,不会用作起始位置。
        //consumer.setStartFromTimestamp(1585047859000L);

        // 为每个分区指定偏移量
        /*Map<KafkaTopicPartition, Long> specificStartOffsets = new HashMap<>();
        specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 0), 23L);
        specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 1), 31L);
        specificStartOffsets.put(new KafkaTopicPartition("qfbap_ods.code_city", 2), 43L);
        consumer1.setStartFromSpecificOffsets(specificStartOffsets);*/
        /**
         *
         * 请注意:当 Job 从故障中自动恢复或使用 savepoint 手动恢复时,
         * 这些起始位置配置方法不会影响消费的起始位置。
         * 在恢复时,每个 Kafka 分区的起始位置由存储在 savepoint 或 checkpoint 中的 offset 确定
         *
         */

        DataStreamSource<String> source = senv.addSource(consumer);
        // TODO
        source.print();
        senv.execute("test kafka connector");
    }
}

参数配置解读

在Demo示例中,给出了详细的配置信息,下面将对上面的参数配置进行逐一分析。

kakfa的properties参数配置
  • bootstrap.servers:kafka broker地址

  • zookeeper.connect:仅kafka0.8版本需要配置

  • group.id:消费者组

  • enable.auto.commit:

    自动偏移量提交,该值的配置不是最终的偏移量提交模式,需要考虑用户是否开启了checkpoint,

    在下面的源码分析中会进行解读

  • auto.commit.interval.ms:偏移量提交的时间间隔,毫秒

  • key.deserializer:

    kafka 消息的key序列化器,如果不指定会使用ByteArrayDeserializer序列化器

  • value.deserializer:

kafka 消息的value序列化器,如果不指定会使用ByteArrayDeserializer序列化器

  • auto.offset.reset:

    指定kafka的消费者从哪里开始消费数据,共有三种方式,

    • 第一种:earliest
      当各分区下有已提交的offset时,从提交的offset开始消费; 无提交的offset时,从头开始消费
    • 第二种:latest
      当各分区下有已提交的offset时,从提交的offset开始消费;无提交的offset时,消费新产生的该分区下的数据
    • 第三种:none
      topic各分区都存在已提交的offset时,从offset后开始消费;只要有一个分区不存在已提交的offset,则抛出异常

    注意:上面的指定消费模式并不是最终的消费模式,取决于用户在Flink程序中配置的消费模式

Flink程序用户配置的参数
  • consumer.setCommitOffsetsOnCheckpoints(true)

​ 解释:设置checkpoint后在提交offset,即oncheckpoint模式,该值默认为true,该参数会影响偏移量的提交方式,下面的源码中会进行分析

  • consumer.setStartFromEarliest()

    解释: 最早的数据开始消费 ,该模式下,Kafka 中的 committed offset 将被忽略,不会用作起始位置。该方法为继承父类FlinkKafkaConsumerBase的方法。

  • consumer.setStartFromGroupOffsets()

    解释:消费者组最近一次提交的偏移量,默认。 如果找不到分区的偏移量,那么将会使用配置中的 auto.offset.reset 设置,该方法为继承父类FlinkKafkaConsumerBase的方法。

  • consumer.setStartFromLatest()

解释:最新的数据开始消费,该模式下,Kafka 中的 committed offset 将被忽略,不会用作起始位置。该方法为继承父类FlinkKafkaConsumerBase的方法。

  • consumer.setStartFromTimestamp(1585047859000L)

解释:指定具体的偏移量时间戳,毫秒。对于每个分区,其时间戳大于或等于指定时间戳的记录将用作起始位置。 如果一个分区的最新记录早于指定的时间戳,则只从最新记录读取该分区数据。在这种模式下,Kafka 中的已提交 offset 将被忽略,不会用作起始位置。

  • consumer.setStartFromSpecificOffsets(specificStartOffsets)

解释:为每个分区指定偏移量,该方法为继承父类FlinkKafkaConsumerBase的方法。

请注意:当 Job 从故障中自动恢复或使用 savepoint 手动恢复时,这些起始位置配置方法不会影响消费的起始位置。在恢复时,每个 Kafka 分区的起始位置由存储在 savepoint 或 checkpoint 中的 offset 确定。

Flink Kafka Consumer源码解读

继承关系

Flink Kafka Consumer继承了FlinkKafkaConsumerBase抽象类,而FlinkKafkaConsumerBase抽象类又继承了RichParallelSourceFunction,所以要实现一个自定义的source时,有两种实现方式:一种是通过实现SourceFunction接口来自定义并行度为1的数据源;另一种是通过实现ParallelSourceFunction接口或者继承RichParallelSourceFunction来自定义具有并行度的数据源。FlinkKafkaConsumer的继承关系如下图所示。
在这里插入图片描述

源码解读

FlinkKafkaConsumer源码

先看一下FlinkKafkaConsumer的源码,为了方面阅读,本文将尽量给出本比较完整的源代码片段,具体如下所示:代码较长,在这里可以先有有一个总体的印象,下面会对重要的代码片段详细进行分析。

public class FlinkKafkaConsumer<T> extends Flink
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flink KafkaSource 可以通过获取 Kafka Topic 的元数据来实现。元数据包括 Topic 的名称、分区数、每个分区的副本数量和分区的 Leader 等信息。 为了获取元数据,我们可以使用 flink-connector-kafka 库提供的 Kafka Consumer API 来连接到 Kafka 集群。具体步骤如下: 1. 创建 Kafka Consumer:使用 flink-connector-kafka 库提供的 Kafka Consumer API 创建一个 Kafka Consumer 实例。在创建实例时,需要配置 Kafka 集群的地址、Topic 的名称以及其他必要参数。 2. 获取 Kafka Topic 的元数据:通过调用 Kafka Consumer 的 `listTopics()` 方法,可以获取到 Kafka 集群中所有的 Topic 和它们的分区信息。该方法返回一个 Map,其中键是 Topic 的名称,值是一个 TopicPartitionInfo 对象,该对象包含了分区的信息。 3. 解析元数据:遍历上一步获取到的 Map,可以获取每个 Topic 的名称和分区数等信息。通过访问 TopicPartitionInfo 对象的方法,可以获取到每个分区的副本数量和 Leader 等元数据。 4. 处理元数据:根据需要,可以将元数据转化为想要的格式或者进行进一步的处理。例如,可以将元数据存储到数据库或者打印到日志中。 通过以上步骤,我们可以使用 Flink KafkaSource 获取 Kafka Topic 的元数据。这些元数据可以帮助我们了解 Topic 的结构以及分区的情况,从而更好地设计和优化 Flink 程序的处理逻辑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值