一、Flink应用开发
Flink作为流批一体的计算引擎,其面对的是业务场景,面向的使用者是开发人员和运维管理人员。
Flink应用程序,也叫Flink作业、FlinkJob.Flink作业包含了两个基本的块:数据流(DataStream)和转换(Tranformation)。DataStream是逻辑概念,为开发者提供了API接口,Transformation是处理行为的抽象,包含了数据的读取、计算、写出。所以Flink的作业中的DataStreamAPI调用,实际上构建了多个由Transformation组成的数据处理流水线(Pipline)。
执行时,Flink应用被映射成DataFlow,由数据流和转换操作组成。每个DataFlow从一个或多个数据源开始,并以一个或多个Sink输出结束。DataFlow本质上是一个有向无环图(DAG),但是允许通过迭代构造特殊形式的有向无环图。
Flink应用由相同的基本部分组成:
- 获取参数(可选)
如果有配置参数,则读取配置参数,可以是命令输入的参数,也可以是配置文件。
- 初始化Stream执行环境
这是必须要做的,读取数据的API依赖于该执行环境。 - 配置参数
读取到的参数可以是执行环境参数或者业务参数。这些参数会覆盖flink.conf中默认的配置参数。
- 读取外部数据
Flink作为分布式执行引擎,本身没有数据存储能力,所以定义了一系列接口、连接器与外部存储进行交互,读写数据。 - 数据处理流程
调用DataStream的API组成数据处理的流程,如调用DataStream.map().filter()……组成一个数据流水线。 - 将处理结果写入外部
在Flink中将数据写入外部的过程叫做Sink,Flink支持写出数据到Kafka、HDFS、Hbase等外部存储。 - 触发执行
StreamExecutionEnvironment#execute是Flink应用执行的触发入口,无论是一般的DataStreamAPI开发还是Table&SQL开发都是如此。
二、API层次
API层次如图:
- 核心底层API
核心底层API提供了Flink的最底层的分布式计算构建块的操作API,包含了ProcessFunction、状态、时间和窗口等操作的API。
ProcessFunction是Flink提供的最具表现力的底层功能接口。Flink提供单流输入的ProcessFunction和双流输入的CoProcessFuntion,能够对单个事件进行计算,也能够按照窗口对时间进行计算。 - 核心开发API(DataStream/DataSet)
DataStream/DataSet使用Fluent风格API,提供了常见数据处理的API接口,如用户指定的各种转换形式,包括连接(Join)、聚合(Aggregation)、窗口(Window)、状态(State)等。
- 声明式DSL API
Table API是以表为中心的声明式领域专用语言(Domain Specified Language,DSL)。表是关系型数据库的概念,用在批处理中。在流计算中,为了引入动态表的概念(Dynamic Table),用来表达数据流表。 - 结构化API
SQL是Flink的结构化API,是最高层次的计算API,与Table API基本等价, 区别在于使用的方式。SQL与Table API可以混合使用,SQL可以操作 Table API 定义的表,Table API也能操作SQL定义的表和中间结果。
三、数据流
数据流是核心数据抽象,表示一个持续产生的数据流。
DataStream体系如图:
DataStreamSource本身就是一个DataStream。DataStreamSink、AsyncDataStream、BroadcastDataStream、BroadcastConnectedDataStream、QueryableDataStream都是对一般DataStream对象封装。
- DataStream
DataStream是Flink数据流的抽象核心,其上定义了对数据流的一系列操作,同时也定义了与其他类型DataStream的相互转换关系。每个DataStream都有一个Transformation对象,表示该DataStream从上游的DataStream使用该Transformation而来。
- DataStreamSource
DataStreamSource是DataStream的起点,DataStreamSource在StreamExecutionEnvironment中创建,由StreamExecutionEnvrionment.addSource(SourceFunction)创建而来,其中SourceFunction中包含了DataStreamSource从数据源读取数据的具体逻辑。
- DataStreamSink
数据从DataSourceStream中读取,经过中间的一系列处理操作,最终需要写出到外部存储,通过DataStream.addSink(SinkFunction)创建而来,其中SinkFunction定义了写出数据到外部存储的逻辑。
- KeyedStream
KeyedStream用来表示根据指定的key进行分组的数据流。一个KeyedStream可以通过调用DataStream.keyBy()来获得。而在KeyedStream上进行任何Transformation都将转变回DataStream。在现实中,KeyedStream把key的信息写入了Transformation中。每条记录只能访问所属Key的状态,其上的聚合函数可以方便地操作和保存对应key的状态。
- WindowedStream & AllWindowedStream
WindowedStream代表了根据key分组且基于WindowAssigner切分窗口的数据流。所以WindowedStream都是从KeyedStream衍生而来的。在WindowedStream上进行任何Transformation也都将转变回DataStream。
- JoinedStreams & CoGroupedStreams
Join是CoGroup的一种特例,JoinedStreams底层 使用CoGroupedStreams来实现。
两者区别如下:
CoGrouped侧重的是Group,对数据进行分组,是对同一个key上的两组集合进行操作。
Join侧重的是数据对,对同一个key的每一对元素进行操作。
- ConnectedStreams
ConnectedStreams表示两个数据流的组合,两个数据流可以类型一样,也可以类型不一样。ConnectedStreams适用于两个有关系的数据流的操作,共享state。
- BroadcastStream & BroadcastConnectedStream
BroadcastStream 实际上是对一个普通DataStream的封装,提供了DataStream的广播行为。
BroadcastConnectedStream 一般由DataStream/KeyedDataStream与BroadcastStream 连接而来,类似于ConnectedStream。
- IterativeStream
IterativeDataStream 是对一个DataStream的迭代操作,从逻辑上来说,包含IterativeStream的Dataflow是一个有向有环图,在底层执行层面上,Flink对其进行了特殊处理。
- AsyncDataStream
AysncDataStream是个工具,提供在DataStream上使用异步函数的能力。
四、数据流API
DataStreamAPI是Flink流计算的最常用的API,相比于Table & SQL API更加底层。
4.1 数据读取
数据读取的API定义在StreamExecutionEnvironmanet,这是Flink流计算应用的起点,第一个DataStream就是从数据读取API中构造出来的。
- 从内存中读取
- 文件中读取
- Socke接入数据
- 自定义读取
4.2 处理数据
DataStreamAPI 使用Fluent风格处理数据,在开发的时候其实是在编写一个DataStream转换过程,形成了DataStream处理链。
从图中可以看到,并不是所有的DataStream都可以互相转换。
- Map
接收1个元素,输出1个元素。Map应用在DataStream上,输出结果为DataStream。 DataStream#map运算对应的是MapFunction,其类泛型为MapFunction,T代表输入数据类型,O代表操作结果输出类型。
- FlatMap
接收1个元素,输出0、1、...、N个元素。该类运算应用在DataStream上,输出结果为DataStream。DataStream#flatMap对应的接口是FlatMapFuncion,其类泛型为FlatMapFunction,T代表输入数据类型,O代表操作结果输出类型。
- Filter
过滤数据,如果返回true则该元素继续向下传递,如果为false则将该元素过滤掉。该类运算应用在DataStream上,输出结果为DataStream。DataStream#filter接口对应的是FilterFunction,其类泛型为FilterFunction,T代表输出和输出的数据类型。
- KeyBy
将数据流元素进行逻辑上的分组,具有相同Key的记录将被划分到同一组。KeyBy()使用Hash Partition实现。该运算应用在DataStream上,输出结果为KeyedStream。输出的数据流类型为KeyedStream,其中T代表KeyedStream中元素数据类型,KEY代表逻辑Key的数据类型。
注意以下两种数据不能作为key。
- POJO类未重写hashCode(),使用了默认的Object.hashCode()。
- 数组类型。
- Reduce
按照KeyedStream中的逻辑分组,将当前数据与最后一次的Reduce结果进行合并,合并逻辑由开发者自己实现。该类运算应用在KeyedStream上,输出结果为DataStream。ReduceFuntion中T代表KeyedStream中元素的数据类型。