Flink Graph
早期,Batch和Stream的图结构和优化方法有很大的区别,所以批处理使用OptimizedPlan来做Batch相关的优化,使用StreamGraph表达流计算的逻辑,最终都转换为JobGraph,实现了流批的统一。
流计算应用的Graph转换
对于流计算应用来说,首先将DataStreamAPI的调用转换为Transformation,然后经过StreamGraph->JobGraph->ExecutionGraph3层转换(Flink内置的数据结构),最后经过Flink的调度执行,在Flink集群中启动计算任务,形成一个物理执行图,该图是物理执行的Task之间的拓扑关系,但是在Flink中没有对应的Graph数据结构,是运行时的概念。
小结:
Flink执行图分为四层:
StreamGraph -> JobGraph -> ExecutionGraph -> Physical Graph
其中Physical Graph在Flink中没有对应的Graph数据结构,是运行时的概念
批处理应用的Graph转换
对于批处理应用而言,首先将DataSet API的调用转换为OptimizedPlan,然后转换为JobGraph。批处理和流计算在JobGraph上完成了统一。
流图
使用DataStream API开发的应用程序,首先被转换为Transformation,然后被映射为StreamGraph,该图与具体的执行无关,核心是表达计算过程的逻辑。
StreamGraph核心对象
StreamGraph由StreamNode和StreamEdge构成。
StreamNode
StreamNode是StreamGraph中的节点,从Transformation转换而来,可以简单理解为一个StreamNode表示一个算子。
StreamEdge
StreamEdge是StreamGraph中的边,用来连接两个StreamNode,一个StreamNode可以有多个出边,入边,StreamEdge中包含了旁路输出,分区器,字段筛选输出等信息。
StreamGraph生成过程
StreamGraph在FlinkClient中生成,由Flink Client在提交时候触发Flink应用的main,用户编写的业务逻辑组装成Transformation流水线,在最后调用StreamExecutionEnvironment。execute()的时候开始触发StreamGraph的构建。
作业图
JobGraph可以由流计算的StreamGraph和批处理的OptimizedPlan转换而来。流计算中,在StreamGraph的基础上进行了一些优化,如果通过OperatorChain机制将算子合并起来,在执行时,调度在同一个Task线程上,避免数据的跨线程,跨网络的传递。
Operator Chains
没有 shuffle 的多个算子合并在一个 subTask 中,就形成了 Operator Chains,类似于 Spark 中的 Pipeline。每个task被一个线程执行.
将算子链接成task是非常有效的优化:它能减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。链接的行为可以在编程API中进行指定。
JobGraph核心对象
JobGraph的核心对象是JobVertex,JobEdge和IntermediateDataSet。
JobVertex
经过算子融合优化后符合条件的多个StreamNode可能会融合在一起生成一个JobVertex,即一个JobVertex包含一个或多个算子,JobVertex的输入是JobEdge,输出是IntermediateDataSet。
JobEdge
JobEdge是JobGraph中连接IntermediateDataSet和JobVertex的边,表示JobGraph中的一个数据流转通道,其上游数据源是intermediatedataset,下游数据是JobVertex,即数据通过JobEdge由IntermediateDataset传递给目标JobVertex。
JobEdge中的数据分发模式会直接影响执行时Task之间的数据连接关系,是点对点连接还是全连接。
IntermediateDataSet
中间数据集IntermediateDataSet是一种逻辑结构,用来表示JobVertex的输出,即该JobVertex中包含的算子会产生的数据集。不同的执行模式下,其对应的结果分区类型,决定了在执行时刻数据交换的模式。
IntermediateDataSet的个数与该JobVertex对应的StreamNode的出边数量相同,可以是一个或者多个。
JobGraph生成过程
JobGraph的生成入口在StreamGraph中,流计算的JobGraph和批处理的JobGraph的生成逻辑不同,对于流而言,使用的是StreamingJobGraphGenerator,对于批而言,使用的是JobGraphGenerator。
执行图
ExecutionGraph是调度Flink作业执行的核心数据结构,包含了作业中所有并行执行的Task的信息,Task之间的关联关系,数据流转关系。
StreamGraph,JobGraph在Flink客户端生成,然后提交给Flink集群。JobGraph到ExecutionGraph的转换在JobMaster中完成,转换过程中的重要变化如下。
1)加入并行度的概念,成为真正可调度的图结构;
2)生成了与JobVertex对应的ExecutionJobVertex和ExecutionVertex,与IntermediateDataSet对应的IntermediateResult和IntermediateResultPartition等,并行将通过这些类实现。
ExecutionGraph核心对象
ExecutionGraph的核心对象有ExecutionJobVertex,ExecutionVertex,IntermediateResult,IntermediateResultPartition,ExecutionEdge和Execution。
ExecutionJobVertex
该对象和JobGraph中的JobVertex一一对应。该对象还包含一组ExecutionVertex,数量与该JobVertex中所包含的StreamNode的并行度一致,假设StreamNode的并行度为5,那么该ExecutionJobVertex也会包含5个ExecutionVertex。
ExecutionJobVertex原来将一个JobVertex封装成ExecutionJobVertex,并依次创建ExecutionVertex,Execution,IntermediateResult和IntermediateResultPartition,用于丰富ExecutionGraph。
在ExecutionJobVertex的构造函数中,首先是依据对应的JobVertex的并发度,生成对应个数的ExecutionVertex。其中,一个ExecutionVertex代表一个ExecutionJobVertex的并发子Task。然后是将原来JobVertex的中间结果IntermediateDataSet转化为ExecutionGraph中的IntermediateResult。
ExecutionVertex
ExecutionJobVertex中会对作业进行并行化处理,构造可以并行执行的实例,每一个并行执行的实例就是ExecutionVertex。
构造ExecutionVertex的同时,也会构建ExecutionVertex的输出IntermediateResult。并且将ExecutionEdge输出为IntermediateResultPartition。
ExecutionVertex的构造函数中,首先会创建IntermediateResultPartition,并通过IntermediateResult.setPartition()建立IntermediateResult和IntermediateResultPartition之间的关系;然后生成Execution,并配置资源相关。
IntermediateResult
IntermediateResult又叫做中间结果集,该对象是个逻辑概念,表示ExecutionJobVertex的输出,和JobGraph中的IntermediateDataSet一一对应,同样,一个ExecutionJobVertex可以有多个中间结果,取决于当前JobVertex有几个出边(JobEdge)。
一个中间结果集包含多个中间结果分区IntermediateResultPartition,其个数等于该JobVertex的并发度,或者叫做算子的并行度。
IntermediateResultPartition
IntermediateResultPartition又叫做中间结果集分区,表示一个ExecutionVertex输出结果,与ExecutionEdge相关联。
ExecutionEdge
表示ExecutionVertex的输入,连接上游产生的IntermediateResultPartition。一个Execution对应于唯一的一个IntermediateResultPartition和一个ExecutionVertex。一个ExecutionVertex可以有多个ExecutionEdge。
Execution
ExecutionVertex相当于每个Task的模板,在真正执行的时候,会将ExecutionVertex中的信息包装成一个Execution,执行一个ExecutionVertex的一次尝试。JobManager和TaskManager之间关于Task部署和Task执行状态的更新是通过ExecutionAttemptID来标识实例的。在发生故障或者数据需要重算的情况下,ExecutionVertex可能会有多个ExecutionAttemptID。一个Execution通过ExecutionAttemptID来唯一标识。