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
测试结果


浙公网安备 33010602011771号