编程模型及核心概念
核心概念概述
Flink 程序是实现了分布式集合转换transformation
(例如过滤filter
、映射mapping
、更新状态updating state
、join
、分组grouping
、定义窗口defining windows
、聚合aggregating
)的规范化程序。集合初始创建自 sources(例如读取文件reading from files
、kafka 主题kafka topics
,或本地local
或内存memory
中的集合)。结果通过 sink 返回,例如,它可以将数据写入(分布式)文件,或标准输出(例如命令行终端)。Flink 程序可以在多种环境中运行,独立运行或嵌入到其他程序中。可以在本地 JVM 中执行,也可以在多台机器的集群上执行。
针对有界和无界两种数据 source 类型,可以使用 DataSet API 来编写批处理程序或使用 DataStream API 来编写流处理程序。本篇指南将介绍这两种 API 通用的基本概念,关于使用 API 编写程序的具体信息请查阅 流处理指南 和 批处理指南。
大数据处理的流程
- MapReduce: input -> map(reduce) -> output
- Storm: input -> Spout(Bolt) -> output
- Spark: input -> Transformation/action -> output
- Spark: input -> Transformation/sink -> output
中间的部分每个框架都会在中间的过程进行自己的操作,但是大体的流程是一样的。
请注意: 当展示如何使用 API 的实际示例时我们使用
StreamingExecutionEnvironment
和DataStream API
。对于批处理,将他们替换为ExecutionEnvironment
和DataSet API
即可,概念是完全相同的。
DataSet&DataStream
Flink 用特有的 DataSet
和 DataStream
类来表示程序中的数据。你可以将他们视为可能包含重复项的不可变immutable
数据集合。对于 DataSet
,数据是有限的,而对于 DataStream
,元素的数量可以是无限的。
这些集合与标准的 Java 集合有一些关键的区别。首先它们是不可变的,也就是说它们一旦被创建就不能添加或删除元素了。你也不能简单地检查它们内部的元素。
在 Flink 程序中,集合最初通过添加数据 source 来创建,通过使用诸如 map
、filter
等 API 方法对数据 source 进行转换从而派生新的集合。
Flink编程模型(剖析一个 Flink 程序)
Flink 程序看起来像是转换数据集合的规范化程序。每个程序由一些基本的部分组成:
- 获取执行环境;
- 加载/创建初始数据;
- 指定对数据的转换操作;transformation
- 指定计算结果存放的位置;sink
- 触发程序执行;
Java
我们现在将概述每个步骤,详细信息请参阅相应章节。请注意所有 Java DataSet API 的核心类可以在这个包 org.apache.flink.api.java 中找到,同时 Java DataStream API 可以在这个包 org.apache.flink.streaming.api 中找到。
StreamExecutionEnvironment
是所有 Flink 程序的基础。你可以使用它的这些静态方法获取:
getExecutionEnvironment()
createLocalEnvironment()
createRemoteEnvironment(String host, int port, String... jarFiles)
通常你只需要使用 getExecutionEnvironment()
,因为它会根据上下文环境完成正确的工作:如果你在 IDE 中执行程序或者作为标准的 Java 程序来执行,它会创建你的本机执行环境;如果你将程序封装成 JAR 包,然后通过命令行调用,Flink 集群管理器会执行你的 main 方法并且 getExecutionEnvironment()
会返回在集群上执行程序的执行环境。
针对不同的数据 source,执行环境有若干不同读取文件的方法:你可以逐行读取 CSV 文件,或者使用完全自定义的输入格式。要将文本文件作为一系列行读取,你可以使用:
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream<String> text = env.readTextFile("file:///path/to/file");
这样你会得到一个 DataStream 然后对其应用转换操作从而创建新的派生 DataStream。
通过调用 DataStream 的转换函数来进行转换。下面是一个映射转换的实例:
DataStream<String> input = ...;
DataStream<Integer> parsed = input.map(new MapFunction<String, Integer>() {
@Override
public Integer map(String value) {
return Integer.parseInt(value);
}
});
这会通过把原始数据集合的每个字符串转换为一个整数创建一个新的 DataStream。
一旦你得到了包含最终结果的 DataStream,就可以通过创建 sink 将其写入外部系统。如下是一些创建 sink 的示例:
writeAsText(String path)
print()
当设定好整个程序以后你需要调用 StreamExecutionEnvironment
的 execute()
方法触发程序执行。至于在你的本机触发还是提交到集群运行取决于 ExecutionEnvironment
的类型。
execute()
方法返回 JobExecutionResult
,它包括执行耗时和一个累加器的结果。
如果你不需要等待作业的结束,只是想要触发程序执行,你可以调用 StreamExecutionEnvironment
的 executeAsync()
方法。这个方法将返回一个 JobClient
对象,通过 JobClient
能够与程序对应的作业进行交互。作为例子,这里介绍通过 executeAsync()
实现与 execute()
相同行为的方法。
final JobClient jobClient = env.executeAsync();
final JobExecutionResult jobExecutionResult = jobClient.getJobExecutionResult(userClassloader).get();
有关流数据的 source 和 sink 以及有关 DataStream 支持的转换操作的详细信息请参阅流处理指南。
有关批数据的 source 和 sink 以及有关 DataSet 支持的转换操作的详细信息请参阅批处理指南。
Scala
我们现在将概述每个步骤,详细信息请参阅相应章节。请注意所有 Scala DataSet API 可以在这个包 org.apache.flink.api.scala 中找到,同时,所有 Scala DataStream API 可以在这个包 org.apache.flink.streaming.api.scala 中找到。
StreamExecutionEnvironment
是所有 Flink 程序的基础。你可以使用它的这些静态方法获取:
getExecutionEnvironment() 直接获取
createLocalEnvironment() 创建本地的方式获取
createRemoteEnvironment(host: String, port: Int, jarFiles: String*) 创建远程的环境
通常只需要使用 getExecutionEnvironment()
,因为它会根据上下文环境完成正确的工作,如果你在 IDE 中执行程序或者作为标准的 Java 程序来执行,它会创建你的本机执行环境。如果你将程序封装成 JAR 包,然后通过命令行调用,Flink 集群管理器会执行你的 main 方法并且 getExecutionEnvironment()
会返回在集群上执行程序的执行环境。
针对不同的数据 source,执行环境有若干不同的读取文件的方法:你可以逐行读取 CSV 文件,或者使用完全自定义的输入格式。要将文本文件作为一系列行读取,你可以使用:
val env = StreamExecutionEnvironment.getExecutionEnvironment()
val text: DataStream[String] = env.readTextFile("file:///path/to/file")
如此你会得到一个 DataStream 然后对其应用转换操作从而创建新的派生 DataStream。
通过调用 DataStream 的转换函数来进行转换。下面是一个映射转换的实例:
val input: DataSet[String] = ...
val mapped = input.map {
x => x.toInt }
这会通过把原始数据集合的每个字符串转换为一个整数创建一个新的 DataStream。
一旦你得到了包含最终结果的 DataStream,就可以通过创建 sink 将其写入外部系统。如下是一些创建 sink 的示例:
writeAsText(path: String)
print()
当设定好整个程序以后你需要调用 StreamExecutionEnvironment
的 execute()
方法触发程序执行。至于在你的本机触发还是提交到集群运行取决于 ExecutionEnvironment
的类型。
execute()
方法返回 JobExecutionResult
,它包括执行耗时和一个累加器的结果。
如果你不需要等待作业的结束,只是想要触发程序执行,你可以调用 StreamExecutionEnvironment
的 executeAsync()
方法。这个方法将返回一个 JobClient
对象,通过 JobClient
能够与程序对应的作业进行交互。作为例子,这里介绍通过 executeAsync()
实现与 execute()
相同行为的方法。