Flink Data Source

  Flink Data Source 用于定义 Flink 程序的数据来源。

基于文件构建

  1. readTextFile(path):按照 TextInputFormat 格式读取文本文件,并将其内容以字符串的形式返回。示例如下:

env.readTextFile(filePath).print();

  2. readFile(fileInputFormat, path) :按照指定格式读取文件。

       3. readFile(inputFormat, filePath, watchType, interval, typeInformation):按照指定格式周期性的读取文件。其中各个参数的含义如下:

  • inputFormat:数据流的输入格式。
  • filePath:文件路径,可以是本地文件系统上的路径,也可以是 HDFS 上的文件路径。
  • watchType:读取方式,它有两个可选值,分别是 FileProcessingMode.PROCESS_ONCE 和 FileProcessingMode.PROCESS_CONTINUOUSLY:前者表示对指定路径上的数据只读取一次,然后退出;后者表示对路径进行定期地扫描和读取。需要注意的是如果 watchType 被设置为 PROCESS_CONTINUOUSLY,那么当文件被修改时,其所有的内容 (包含原有的内容和新增的内容) 都将被重新处理,因此这会打破 Flink 的 exactly-once 语义。
  • interval:定期扫描的时间间隔。
  • typeInformation:输入流中元素的类型。
final String filePath = "D:\\log4j.properties";
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.readFile(new TextInputFormat(new Path(filePath)),
             filePath,
             FileProcessingMode.PROCESS_ONCE,
             1,
             BasicTypeInfo.STRING_TYPE_INFO).print();
env.execute();

  基于集合构建

  1. fromCollection(Collection):基于集合构建,集合中的所有元素必须是同一类型。示例如下:

env.fromCollection(Arrays.asList(1,2,3,4,5)).print();
  def collectionStream(env: StreamExecutionEnvironment): Unit = {
    val data = 1 to 10
    env.fromCollection(data)
      .map(x => x + 1)
      .print()
    env.execute("collection stream")
  }

      2. fromElements(T ...): 基于元素构建,所有元素必须是同一类型。示例如下:

env.fromElements(1,2,3,4,5).print();

  3. generateSequence(from, to):基于给定的序列区间进行构建。示例如下:

env.generateSequence(0,100);

  4. fromCollection(Iterator, Class):基于迭代器进行构建。第一个参数用于定义迭代器,第二个参数用于定义输出元素的类型。使用示例如下:

env.fromCollection(new CustomIterator(), BasicTypeInfo.INT_TYPE_INFO).print();

  其中 CustomIterator 为自定义的迭代器,这里以产生 1 到 100 区间内的数据为例,源码如下。需要注意的是自定义迭代器除了要实现 Iterator 接口外,还必须要实现序列化接口 Serializable ,否则会抛出序列化失败的异常:

import java.io.Serializable;
import java.util.Iterator;

public class CustomIterator implements Iterator<Integer>, Serializable {
    private Integer i = 0;

    @Override
    public boolean hasNext() {
        return i < 100;
    }

    @Override
    public Integer next() {
        i++;
        return i;
    }
}

  5. fromParallelCollection(SplittableIterator, Class):方法接收两个参数,第二个参数用于定义输出元素的类型,第一个参数 SplittableIterator 是迭代器的抽象基类,它用于将原始迭代器的值拆分到多个不相交的迭代器中。 

基于 Socket 构建

  Flink 提供了 socketTextStream 方法用于构建基于 Socket 的数据流,socketTextStream 方法有以下四个主要参数:

  • hostname:主机名;
  • port:端口号,设置为 0 时,表示端口号自动分配;
  • delimiter:用于分隔每条记录的分隔符;
  • maxRetry:当 Socket 临时关闭时,程序的最大重试间隔,单位为秒。设置为 0 时表示不进行重试;设置为负值则表示一直重试。示例如下:
 env.socketTextStream("192.168.0.229", 9999, "\n", 3).print();

CSV文件

  person.csv文件内容

name,age,job
jim,25,java
lucy,22,C#
jack,26,php

  scala文件

  def csvFile(env: ExecutionEnvironment): Unit = {
    val filePath = "d:/person.csv";
    import org.apache.flink.api.scala._
    env.readCsvFile[(String, Int, String)](filePath, ignoreFirstLine = true).print()

    env.readCsvFile[Person](filePath, ignoreFirstLine = true, includedFields = Array(0, 1)).print()
  }

  case class Person(name: String, age: Int)

自定义 Data Source

  自定义的数据源必须要实现 SourceFunction 接口,这里以产生 [0 , 1000) 区间内的数据为例,代码如下:

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

env.addSource(new SourceFunction<Long>() {
    
    private long count = 0L;
    private volatile boolean isRunning = true;

    public void run(SourceContext<Long> ctx) {
        while (isRunning && count < 1000) {
            // 通过collect将输入发送出去 
            ctx.collect(count);
            count++;
        }
    }

    public void cancel() {
        isRunning = false;
    }

}).print();
env.execute();

2.2 ParallelSourceFunction 和 RichParallelSourceFunction

  通过 SourceFunction 实现的数据源是不具有并行度的,即不支持在得到的 DataStream 上调用 setParallelism(n) 方法,此时会抛出如下的异常:

Exception in thread "main" java.lang.IllegalArgumentException: Source: 1 is not a parallel source

  如果要实现具有并行度的输入流,则需要实现 ParallelSourceFunction 或 RichParallelSourceFunction 接口,其与 SourceFunction 的关系如下图:

  

    ParallelSourceFunction 直接继承自 ParallelSourceFunction,具有并行度的功能。RichParallelSourceFunction 则继承自 AbstractRichFunction,同时实现了 ParallelSourceFunction 接口,所以其除了具有并行度的功能外,还提供了额外的与生命周期相关的方法,如 open() ,closen() 。

Streaming Connectors

  在所有 DataSource 连接器中,使用的广泛的就是 Kafka,所以这里我们以其为例,使用的 Kafka 版本为 kafka_2.12-2.2.0,添加的依赖如下:

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-kafka_${scala.binary.version}</artifactId>
            <version>${flink.version}</version>
        </dependency>

  以最简单的场景为例,接收 Kafka 上的数据并打印,代码如下:

final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties properties = new Properties();
// 指定Kafka的连接位置
properties.setProperty("bootstrap.servers", "hadoop001:9092");
// 指定监听的主题,并定义Kafka字节消息到Flink对象之间的转换规则
DataStream<String> stream = env
    .addSource(new FlinkKafkaConsumer<>("flink-stream-in-topic", new SimpleStringSchema(), properties));
stream.print();
env.execute("Flink Streaming");

       参数 topic定义从哪些主题中读取数据。可以是一个topic,也可以是 topic列表,还可以是匹配所有想要读取的 topic 的正则表达式。当从多个 topic 中读取数据时,Kafka 连接器将会处理所有 topic 的分区,将这些分区的数据放到一条流中去。

       第二个参数是一个DeserializationSchema 或者KeyedDeserializationSchema。Kafka 消息被存储为原始的字节数据,需要反序列化成 Java 或者 Scala 对象。上面代码中使用的 SimpleStringSchema,是一个内置的 DeserializationSchema,只是将字节数组简单地反序列化成字符串。DeserializationSchema 和 KeyedDeserializationSchema 是公共接口,所以我们也可以自定义反序列化逻辑。

       第三个参数是一个 Properties 对象,设置了Kafka 客户端的一些属性。

创建topic

# 创建用于测试主题
bin/kafka-topics.sh --create \
                    --bootstrap-server hadoop001:9092 \
                    --replication-factor 1 \
                    --partitions 1  \
                    --topic flink-stream-in-topic

# 查看所有主题
 bin/kafka-topics.sh --list --bootstrap-server localhost:9092

        启动 Producer

        启动一个 Kafka 生产者,用于发送测试数据:

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-topic

   测试结果

 

  

posted on 2021-09-12 22:52  溪水静幽  阅读(268)  评论(0)    收藏  举报