【白话Flink基础理论】Flink运行时(Runtime)架构(二)作业调度&资源管理

——wirte by 橙心橙意橙续缘,

前言

白话系列
————————————————————————————
也就是我在写作时完全不考虑写作方面的约束,完全把自己学到的东西、以及理由和所思考的东西等等都用大白话诉说出来,这样能够让信息最大化的从自己脑子里输出并且输入到有需要的同学的脑中。PS:较为专业的地方还是会用专业口语诉说,大家放心!

白话Flink系列
————————————————————————————
主要是记录本人(国内某985研究生)在Flink基础理论阶段学习的一些所学,更重要的是一些所思所想,所参考的视频资料或者博客以及文献资料均在文末放出.由于研究生期间的课题组和研究方向与Flink接轨较多,而且Flink的学习对于想进入大厂的同学们来说也是非常的赞,所以该系列文章会随着本人学习的深入来不断修改和完善,希望大家也可以多批评指正或者提出宝贵建议。

到底什么是运行时?
——————————————
运行时,也就是RUNTIME,是一个分布式数据流作业执行引擎,按照我的理解就是他是Flink客户端从提交任务,然后这个任务生成JobGraph,到JobManage然后通过调度taskmanage执行一直到结束的整个过程中所需要的全部,包括一些必要的组件(静态),任务调度,资源管理,分布式通信,数据传输、节点同步(动态)等一系列的过程。所以对runtime这一块必须要有清晰深刻的了解,这对于后续进行一些高级的Flink优化操作十分重要!!!
————————————————————————————————
【白话Flink理论】Flink运行时(Runtime)架构(一)整体架构&基本组件&任务提交流程中我们简单地提到过资源管理和作业调度,但是对于这两块的具体含义应该还是非常混乱的,所以单独开这一章节来梳理一下

大家可以看到我把作业调度和资源管理放到了一起来进行讲解,为什么?

很好理解,实际上,作业调度本身就可以看做是对资源Slot和任务Task进行匹配的过程。如上节所述,在 Flink 中,资源是通过TaskManager上的插槽 Slot 来表示的,每个 Slot 可以用来执行不同的 Task。而在另一端,任务即 Job 中实际的 Task,它包含了待执行的用户逻辑。调度的主要目的就是为了给 Task 找到匹配的 Slot。逻辑上来说,每个 Slot 都应该有一个向量来描述它所能提供的各种资源的量,每个 Task 也需要相应的说明它所需要的各种资源的量。但是实际上在 1.9 之前,Flink 是不支持细粒度的资源描述的,而是统一的认为每个 Slot 提供的资源和 Task 需要的资源都是相同的。从 1.9 开始,Flink 开始增加对细粒度的资源匹配的支持的实现,但这部分功能目前仍在完善中。

也就是说1.9之前,Flink直接地将1个task放入到1个Slot中进行执行,但我们很清楚不同的Task对资源的需求肯定是不同的,所以这就需要更加细粒度地来衡量资源的多少,从而不至于造成等待的现象。

资源管理

资源管理过程的详细解释

作业调度的基础是首先提供对资源的管理,因此我们首先来看下 Flink 中资源管理的实现。


在这里插入图片描述

该图为资源管理的详细过程表示

如上图所示,Flink 中的资源是由 TaskManager 上的 Slot 来表示的,所以资源管理其实就是Slot管理。在上图中我们可以看到,在 ResourceManager 中,有一个子组件叫做 SlotManager,顾名思义,它维护了当前集群中所有 TaskManager 上的 Slot 的信息与状态,如哪个 Slot 在哪个 TaskManager 中,该 Slot 当前是否空闲等。

  • 当 JobManger 来为特定 Task 申请资源的时候,根据当前是 Per-job 还是 Session 模式(在上一节有提到过),ResourceManager 可能会去申请资源来启动新的 TaskManager 。
  • 当 TaskManager 启动之后,它会通过服务发现找到当前活跃的 ResourceManager 并进行注册。在注册信息中,会包含该 TaskExecutor中所有 Slot 的信息。
  • ResourceManager 收到注册信息后,其中的 SlotManager 就会记录下相应的 Slot 信息。
  • 当 JobManager 为某个 Task 来申请资源时, SlotManager 就会从当前空闲的 Slot 中按一定规则选择一个空闲的 Slot 进行分配。
  • 当分配完成后,RM 会首先向 TaskManager 发送 RPC 要求将选定的 Slot 分配给特定的 JobManager。
  • TaskManager 如果还没有执行过该 JobManager 的 Task 的话,它需要首先向相应的 JobManager 建立连接,然后发送提供 Slot 的 RPC 请求。
  • 在 JobManager 中,所有 Task 的请求会缓存到 SlotPool 中。当有 Slot 被提供之后,SlotPool 会从缓存的请求中选择相应的请求并结束相应的请求过程。
  • 当 Task 结束之后,无论是正常结束还是异常结束,都会通知 JobManager 相应的结束状态,然后在 TaskManager 端将 Slot 标记为已占用但未执行任务的状态。JobManager 会首先将相应的 Slot 缓存到 SlotPool 中,但不会立即释放。

为什么要这样嗯? 因为这种方式避免了如果将 Slot 直接还给 ResourceManager,在任务异常结束之后需要重启时,需要立刻重新申请 Slot 的问题。通过延时释放,Failover 的 Task 可以尽快调度回原来的 TaskManager,从而加快 Failover 的速度。当 SlotPool 中缓存的 Slot 超过指定的时间仍未使用时,SlotPool 就会发起释放该 Slot 的过程。与申请 Slot 的过程对应,SlotPool 会首先通知 TaskManager 来释放该 Slot,然后 TaskExecutor 通知 ResourceManager 该 Slot 已经被释放,从而最终完成释放的逻辑。

这个超时是怎么实现的呢?主要是Flink除了正常的通信逻辑外,在 ResourceManager 和 TaskManager 之间还存在定时的心跳消息来同步 Slot 的状态。在分布式系统中,消息的丢失、错乱不可避免,这些问题会在分布式系统的组件中引入不一致状态,如果没有定时消息,那么组件无法从这些不一致状态中恢复。此外,当组件之间长时间未收到对方的心跳时,就会认为对应的组件已经失效,并进入到 Failover 的流程。

作业调度

JobManager数据结构

运行在各个TaskManager的slot中任务的调度是通过JobManager完成,除此之外,JobManager还负责失败任务的重启等,所以我们需要先了解一下JobManager的数据结构,也就是DAG图表示。

当JobManager接受到Client生成的JobGraph(JobGraph 是数据流的表现形式,包括图结点JobVertex,也就是算子和中间结果IntermediateDataSet,每个算子都有诸如并行度和执行代码等属性)会将其转换为ExecutionGraph,两者之间的关系如下图所示。
在这里插入图片描述

上图体现了在JobManager中JobGraph->executionGraph的转换过程,而作业调度正是通过执行图来进行的

对每个 JobVertex,可以看成是经过算子优化组成的一个个operator chain(每个operator chain可以是一个或多个算子)和相关信息组成,而ExecutionVertex可以看做是JobVertex的并行版,假设组成一个JobVertex的operator chain的并行度为100,则在ExecutionGraph中,ExecutionVertex有100个,对应关系可以多看看上图。

在JobGraph转换到ExecutionGraph的过程中,主要发生了以下转变:

  • 加入了并行度的概念,成为真正可调度的图结构
  • 生成了与JobVertex对应的ExecutionJobVertex,ExecutionVertex,与IntermediateDataSet对应的IntermediateResult和IntermediateResultPartition等,并行将通过这些类实现。
  • 每个 ExecutionGraph 都有一个与其相关联的作业状态。此作业状态指示作业执行的当前状态,具体的状态图如下:
    在这里插入图片描述

图中各个状态说明情况很清楚,就不详细说明,需要注意的是暂停状态的作业将可能不会被完全清理。暂停状态(suspended)仅处于本地终止状态,在Flink的HA模式下,意味着作业的执行仅在相应的 JobManager 上终止,但集群的另一个 JobManager 可以从持久的HA存储中恢复这个作业并重新启动。

作业调度过程的详细解释

在上述Slot 管理,也就是资源管理的基础上,Flink 才可以将 Task 调度到相应的 Slot 当中。如上文所述,Flink 尚未完全引入细粒度的资源匹配,默认情况下,每个 Slot 可以分配给一个 Task。但是,这种方式在某些情况下会导致资源利用率不高。如下图所示,假如 A、B、C 依次执行计算逻辑,那么给 A、B、C 分配分配单独的 Slot 就会导致资源利用率不高(因为会有slot与slot之间,甚至是跨taskmanager之间的通信时延问题)为了解决这一问题,Flink 提供了 Share Slot 的机制。

Share Slot(插槽共享)

如下图所示,基于 Share Slot,每个 Slot 中可以部署来自不同 JobVertex 的多个任务,但是不能部署来自同一个 JobVertex 的 Task。如图5所示,每个 Slot 中最多可以部署同一个 A、B 或 C 的 Task,但是可以同时部署 A、B 和 C 的各一个 Task。当单个 Task 占用资源较少时,Share Slot 可以提高资源利用率。 此外,Share Slot 也提供了一种简单的保持负载均衡的方式。
在这里插入图片描述

使用 Share Slot 可以在每个 Slot 中部署来自不同 JobVertex 的多个Task.

基于上述 Slot 管理和分配的逻辑,JobManager 负责维护作业中 Task执行的状态。前面也提到过,Client 端会向 JobManager 提交一个 JobGraph,它代表了作业的逻辑结构。JobManager 会根据 JobGraph 按并发展开,从而得到 JobManager 中关键的 ExecutionGraph。ExecutionGraph 的结构如上面那副图所示,与 JobGraph 相比,ExecutionGraph 中对于每个 Task 与中间结果等均创建了对应的对象,从而可以维护这些实体的信息与状态。
下面这幅图也能很好地说明这个图的转换关系
在这里插入图片描述

Flink 中的 JobGraph 与 ExecutionGraph。ExecutionGraph 是 JobGraph 按并发展开所形成的,它是 JobMaster 中的核心数据结构

下面我们将通过2个问题的解答来具体说明Flink是怎么根据执行图来进行任务调度的。

1. 在Flink中1个job是如何划分task的?

Flink是通过task slot的来定义执行资源的,为优化资源的利用率,Flink通过slot共享,可以将多个连续的task任务组成的一个pipeline放在一个slot中运行。当任务并行度>1时,并行任务中的每个pipeline就会分配到一个slot去执行,这样就会有一个问题,若是任务的并行度(也就是任务中算子的最大并行度)大于集群中slot的个数了,会咋办?首先,毫无疑问的一点是集群中的slot中都会有pipeline在跑;其次,多的任务就会等待现有的运行结束再去运行。下面结合官网中提供的例子说明一般情况下pipeline的分配情况。

在下图中,一个pipeline由Source - Map - Reduce组成,其中MapFunction的并行度为4,ReduceFunction的并行度为3,集群有两个TaskManager,其中每个TaskManager有3个slot。
在这里插入图片描述
图中,每一个pipeline由一个颜色表示,其中包含3个小圈,每一个圈代表一个算子,ReduceFunction的并行度为3,而MapFunction的为4,所以从Map->Reduce会发生shuffer。图中,任务会以ExecutionVertex 组成的 DAG 图的形式分配到两个TaskManage的slot中,在TaskManager2的slot中,运行在其中一个slot的DAG仅有两个ExecutionVertex ,这里会发生网络shuffle。
  
2. 在 Flink 中按什么顺序来调度 Task?

在一个 Flink Job 中是包含多个 Task 的,因此另一个关键的问题是在 Flink 中按什么顺序来调度 Task。如下图 所示,目前 Flink 提供了两种基本的调度逻辑,即 Eager 调度与 Lazy From Source

  • Eager 调度如其名子所示,它会在作业启动时申请资源将所有的 Task 调度起来。这种调度算法主要用来调度可能没有终止的流作业。
  • Lazy From Source 则是从 Source 开始,按拓扑顺序来进行调度。简单来说,Lazy From Source 会先调度没有上游任务的 Source 任务,当这些任务执行完成时,它会将输出数据缓存到内存或者写入到磁盘中。然后,对于后续的任务,当它的前驱任务全部执行完成后,Flink 就会将这些任务调度起来。这些任务会从读取上游缓存的输出数据进行自己的计算。这一过程继续进行直到所有的任务完成计算。
    在这里插入图片描述

总结

可以看到,Flink的资源管理和任务调度是密不可分的,Flink的资源管理主要是通过管理Slot来进行的,具体组件就会说ResourceManager中的SlotManager来进行的;任务调度则是根据JobManager所生成的执行图中的拓扑顺序来进行的,然后为每个task都分配1个在SlotPool中的Slot。

参考资料

《B站尚硅谷2021年Flink教程java版》

《Flink原理(四)——任务及调度》

《flink原理分析—作业和调度(Jobs and Scheduling)》

《Apache Flink 进阶(一):Runtime 核心机制剖析》

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值