一:逻辑视图与物理视图
Flink程序实际执行前,Flink会将用户变得的代码做一个简单的处理,生成逻辑视图。如图1所示。图中圆圈代表算子,keyBy(),timeWindow(),sum()可以组成一个时间窗口上的聚合操作,被归结为一个算子WindowAggration。FLink提供了编程接口,我们可以使用算自己构建数据流的逻辑视图。逻辑视图只是一种抽象,要将逻辑视图转化为物理执行图才能在分布式环境执行程序。
图1. 逻辑视图
物理视图如图2所示,圆圈部分表示算子在分区上的算子子任务。在分布式环境下,执行计算的单个节点(物理机或虚拟机)被称为实例,一个算子在并行执行时,算子子任务会被分到多个节点上,所以算子子任务又被称为算子实例。算子的并行度是可以设置的。在图中,sink算子的并行度设置为1,其他算子的并行度设置为2,并行度为多少,算子子任务就有多少个。算子子任务是Flink物理执行的基本单位,算子子任务之间是相互独立的,有自己的线程,可能分布在不同的节点上。
图2. 物理执行图
逻辑视图到物理视图的转换过程:StreamGraph->JobGraph->ExcutionGraph->物理执行图。
StreamGraph:表示Flink流处理作业的拓扑结构,由代码生成的最初的图,在StreamGraph中每个节点就是算子;
JobGraph:提交给JobManager的数据结构是JobGraph,StreamGraph优化后生成JobGraph,优化主要是将多个符合条件的节点链接在一起作为一个JobGraph的节点(JobVertex节点),以减少数据交换的传输开销。这个连接过程叫算子链。JobVertex节点包含一到多个算子,它的输出是IntermediateDataSet,是经过算子处理产生的数据集。
ExecutionGraph:JobManager会将JobGraph转化为ExecutionGraph,ExecutionGraph是JobGraph的并行版本。如JobVertex节点的并行度是4,那么它将生成4个ExecutionVertext节点,ExecutionVertex表示一个算子子任务。每个ExecutionVertex会输出一个IntermediateResultPartition,这是单个子任务的输出,ExecutionJobVertex是这些并行子任务的合集。
物理执行图:JobManager根据ExecutionGraph对作业进行调度,在各个TaskManager上部署具体的任务,物理执行图并不是一个具体的数据结构。
图3. 逻辑视图转换为物理视图
三:算子链
在构造物理执行图时,Flink会将一些算子子任务连接在一起,组成算子链。链接后以任务的形式被TaskManager调度执行。这样可以减少算子子任务之间的数据传输开销。任务是TaskManager中的一个线程。
如,Source前向传播到Flatmap时没有发生跨分区的数据交换,就可以将Source和FlatMap两个子任务合在一起,形成一个任务。数据经过KeyBy会发生跨越分区,则不能将KeyBy与其后面的操作链接到一起。
默认设置下,Flink会尽量将更多的子任务链接到一起。Flink可以设置是否启用算子链,或者对那些算子可以使用算子链。
四:数据交换策略
图4. 数据交换策略
不同的算子子任务之间有数据流动或交换,常见的交换策略有一下几种:
前向传播(Forward):前一个算子子任务将数据直接传递给后一个算子子任务,布局没有跨分区交换,减小各类开销。
按key分组(Key-Based):数据以(key,value)二元组的形式存在,该策略将所有数据以key分组,key相同的数据分到一组、发送至同一分区上。如keyBy()。
广播(Broadcast):将数据发送到所有分区上,该策略十分消耗资源。
随机(Random):该策略将数据随机均匀的发送到不同分区上,保证数据平均分配到不同分区上。可以防止数据倾斜。