总的来说,先了解清楚Flink的一些重要工作和通信机制,然后再去剖析一个Flink Job 到底是如何执行的,Flink的 Cluster到底是如何管理和分配slot资源的等等,就比较容易了。
概念解释:
final JobGraph jobGraph = PipelineExecutorUtils.getJobGraph(pipeline, configuration);
Flink的一个Job,最终归根结底,还是构建一个高效率的能用于分布式并行执行的DAG执行图。
Flink的执行图可以分为四层:StreamGraph ===> JobGraph ===> ExecutionGraph ===> PhysicalGraph (物理执行图)
上面这张图清晰的给出了Flink各个图的工作原理和转换过程。其中最后一个物理执行图并非Flink的数据结构,而是程序开始执行后,各个Task分布在不同的节点上,所形成的物理上的关系表示:
那么,设计中为什么要设计这四层执行逻辑呢?它的意义是什么?
1、StreamGraph 是对用户逻辑的映射
2、JobGraph 在StreamGraph 基础上进行了一些优化,比如把一部分操作串成 chain 以提高效率
3、ExecutionGraph 是为了调度存在的,加入了并行处理的概念
4、物理执行结构:真正执行的是 Task 及其相关结构
1、
StreamGraph
就是通过用户编写程序时指定的算子进行逻辑拼接的
简单说:就是进行算子拼接
2、JobGraph
其实就是在 StreamGraph 的基础上做了一定的优化,然后生成的逻辑执行图
简单说:就是把能优化的Operator拼接在一起,放到一个Task中执行的算子的整合和
优化chain在一起形成OperatorChain,类似于Spark Stage切分
3、ExecutionGraph
再把JobGraph进行并行化生成ExecutionGraph
简单说:其实ExecutionGraph 就是JobGraph的并行化版本
4、物理执行图
其实是ExecutionGraph 调度运行之后形成的分布,当一个Flink Stream Job中的所有
Task 都被调度执行起来了之后的状态
简单说:就是最终运行状态图
两个重要的转化
1、StreamGraph 转变成 JobGraph:帮我们把上下游两个相邻算子如果能chain到一起,则chain到一起做优化
2、JobGraph转变成ExecutionGraph:chain到一起的多个Operator就会组成一个OperatorChain,当OperatorChain 执行的时候,到底要执行多少个Task,则就需要把 DAG 进行并行化变成实实在在的 Task 来调度
StreamGraph:根据用户通过Stream API 编写的代码生成的最初的图。Flink把每一个算子 transform 成一个对流的转换(比如 SingleOutputStreamOperator,它就是一个 DataStream 的子类),并且注册到执行环境中,用于生成 StreamGraph。
它包含的主要抽象概念有:
1、StreamNode:用来代表 operator 的类,并具有所有相关的属性,如并发度、入边和出边等。
2、StreamEdge:表示连接两个 StreamNode的边
JobGraph:StreamGraph 经过优化后生成了 JobGraph,提交给 JobManager 的数据结构它包含的主要抽象概念有:
1、JobVertex:经过优化后符合条件的多个 StreamNode 可能会chain 在一起生成一个 JobVertex,即一个JobVertex 包含一个或多个 operator,JobVertex 的输入是 JobEdge,输出是 IntermediateDataSet。
2、IntermediateDataSet:表示JobVertex 的输出,即经过 operator 处理产生的数据集,producer 是JobVertex,consumer 是 JobEdge。
3、JobEdge:代表了job graph 中的一条数据传输通道。Source 是 IntermediateDataSet,targe 是 JobVertex。即数据通过 JobEdge 由IntermediateDataSet 传递给目标 JobVertex。
ExecutionGraph:JobManager(JobMaster) 根据 JobGraph 生成 ExecutionGraph。ExecutionGraph 是JobGraph 的并行化版本,是调度层最核心的数据结构。
它包含的主要抽象概念有:
1、ExecutionJobVertex:和JobGraph中的JobVertex一一对应,每一个ExecutionJobVertex都有和并发度一样多的 ExecutionVertex。
2、ExecutionVertex:表示ExecutionJobVertex 的其中一个并发子任务,输入是ExecutionEdge,输出的是IntermediateResultPartition。
3、IntermediateResult:和 JobGraph 中的 IntermediateDataSet一一对应。一个 IntermediateResult包含多个 IntermediateResultPartition,其个数等于该 operator 的并发度。
4、IntermediateResultPartition:表示ExecutionVertex的一个输出分区,producer是ExecutionVertex,consumer是若干个ExecutionEdge。
5、ExecutionEdge:表示ExecutionVertex的输入,source 是IntermediateResultPartition,targe 是 ExecutionVertex。source 和 target 都只能是一个 EdgeManager
6、Execution:是执行一个 ExecutionVertex 的一次尝试。当发生故障或者数据需要重算的情况下 ExecutionVertex 可能会有多个 ExecutionAttemptID。一个 Execution 通过 ExecutionAttemptID 来唯一表示。JM 和 TM 之间关于 task 的部署和 task status 的更新都是通过 ExecutionAttemptID 来确定消息接受者。
物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图
”,并不是一个具体的数据结构。
它包含的主要抽象概念有:
1、Task:Execution 被调度后在分配的 TaskManager 中启动对应的 Task。Task包裹了具有用户执行逻辑的 operator。
2、ResultPartition:代表由一个 Task 的生成的数据,和 ExecutionGraph 中的IntermediateResultPartition 一一对应。
3、ResultSubPartition:是 ResultPartition 的一个子分区,每个 ResultPartition 包含多个 ResultSubPartition,其数据要由下游消费 Task 数和 DistributionPattern 来决定。
4、InputGate:代表 Task 的输入封装,和 JobGraph 中 JobEdge 一一对应,每个 InputGate 消费了一个或者多个的 ResultPartition。
5、InputChannel:每个 InputGate 会包含一个以上的 InputChannel,和 ExecutionGraph 中的 ExecutionEdge 一一对应,也和 ResultSubpartition一对一的相连,即一个 InputChannel 接收一个 ResultSubpartition 的输出。
- JobManger 负责整个 Flink 集群人物的调度以及资源的管理。
- JobManger 从客户端中获取提交的应用,然后根据集群中TaskManger 上 TaskSlot 的使用情况,为提交的应用分配相应的TaskSlot 资源,并命令TaskManager 启动从客户端中获取的应用。
- 当任务完成后,Flink 会将任务执行的信息反馈给客户端,并且释放掉 TaskManager 中的资源以供下一次提交任务使用。
ResourceManager
主要负责 任务管理器(TaskManager)的插槽(Slot)
,TaskManager插槽是Flink中定义的处理资源单元
提供了
不同的资源管理器比如Yarn,K8s,以及 Standalone 部署。
管理资源
- 为不同环境
提供
各种的资源管理器分配
空闲插槽终止
空闲TaskManager,释放资源
多个
TaskManager 运行。任务执行
和对应任务在每个节点上的 资源申请和管理
。
- 通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含了一定数量的插槽(slots)。
- 插槽的数量限制了TaskManager能够执行的任务数量。
- 启动之后,TaskManager会向资源管理器注册它的插槽。
- 收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给作业管理器调用,作业管理器就可以向插槽分配任务。
- 在执行过程中,一个TaskManager可以跟其他运行同一应用程序的TaskManager交换数据,同时TaskManager 之间的数据交互都是通过数据流的方式进行的。
在一些应用场景中,对于集群资源分配和占用的方式,可能会有特定的需求。Flink为各种场景提供了不同的部署模式,主要有以下三种
它们的区别主要在于:集群的生命周期和资源分配方式不同、以及应用程序的 main() 方法到底在哪执行。客户端(Client)还是 JobManager。
这里我们重点探讨 Flink On Yarn。
以往,我们接触过非常多的大数据技术栈相关的框架,用的比较多的大数据相关组件,常用的RPC实现技术如下:
技术组件 | RPC实现 |
---|---|
Hadoop | NIO + Protobuf (Protobuf即Protocol Buffers,是Google公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的, 高效的用于序列化数据的协议) |
Hbase | Hbase2.x以前:NIO + ProtoBuf,Hbase2.x会议后:Netty |
ZooKeeper | BIO(Blocking I/O,同步阻塞I/O模式) + NIO (New I/O,同步非阻塞的I/O模型) + Netty |
Spark | Spark-2.x 以前 基于 Akka Actor,Spark2.x往后基于 Netty RpcEndpoint |
Flink | Akka(组件中间)+ Netty (operator) |
Kafka | NIO |
在Flink中主从节点之间的通信组件,即:akka,可以通过Flink web ui进行确认
[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(im-log.animg.cn/95abeydD1907ef2e234a8895036959ea70(aa0.png:tps://img-blog.csdnimg.cn/951907ef2e234a889e553506ea909aa0.png)]
为什么Flink使用了两套通信框架呢?这是因为Flink节点与节点之间,组件与组件之间通信采用的是Akka,但是数据交换,比如算子与算子之间的数据交换采用的是Netty,比如Flink中有JobManager,还有TaskManager从节点,而JobManager主节点里面有一些组件,比如JobMaster,Dispatcher等,组件与组件之间通信采用的是Akka,而Netty是算子与算子之间,比如Map算子后面跟着Filter,Filter后面跟着reducekeyby等操作这些算子之间采用的是Netty来通信的。
Fink 内部节点之间的通信是用Akka,比如JobManager 和 TaskManager之间的通信,而operator之间的数据传输是采用的Netty
Flink通过Akka
进行的分布式通信的实现,有远程过程调用都实现为异步消息,这主要影响组件:JobManager,TaskManager 和JobClient.
RPC框架是Flink任务运行的基础,Flink整个RPC框架基于Akka实现,并对Akka中的ActorSystem, Actor 进行了封装和使用,Flink整个通信框架的组件主要由 RpcEndpoint,RpcService,RpcServer,AkkaInvocationHandler,AkkaRpcActor等构成。