【Flink从入门到精通 03】运行时架构

知其然,知其所以然,通过之前的文章,我们已然了解了如何编写一个Flink程序并提交运行,那么这个程序是如何在集群中运行的呢?今天,就给大家分享下Flink的运行时架构。

01 运行时组件

在这里插入图片描述

观察上图,可以看到,Flink作业在执行过程中会涉及到作业管理资源管理任务管理分发器等部分。我们来逐个分析:

(1)作业管理器(JobManager)

作业管理器是控制应用程序的主进程。

  • 通常涉及以下结构:

    • 作业图(JobGraph)
    • 逻辑数据流图(logical dataflow graph)
    • 类、库及其他资源JAR包
  • 对Client提交作业的处理流程:

    1. JobGraph转换成一个物理层面的的数据流图,即执行图(ExecutionGraph),包含了所有可并发执行的任务

    2. JobManager向资源管理器(ResourceManager)请求执行任务必要的资源,即TaskManager上的slot

    3. 获取足够资源后,将执行图分发到真正运行它们的TaskManager

    4. 运行过程中,JobManager负责所有需要中央协调的操作,如检查点(checkpoint)的协调

(2)资源管理器(ResourceManager)

资源管理器主要负责管理任务管理器(TaskManager)的插槽(slot),TaskManager slot是Flink中定义的处理资源单元。

  • 不同环境和资源管理工具提供了不同的资源管理器,在国内通常以Yarn作为Flink集群的资源管理器。

  • 流程:

    1. JobManager向ResourceManager申请插槽资源,ResourceManager将有空闲插槽的TaskManager分配给JobManager

    2. 如果ResourceManager没有足够的插槽来满足JobManager请求,还可以向资源提供平台发起会话,以提供启动TaskManager进程的容器

    3. ResourceManager还负责终止空闲的TaskManager,释放计算资源

(3)任务管理器(TaskManager)

任务管理器是Flink中的真正进行工作的进程。

  • Flink中会有多个TaskManager运行,每一个TaskManager都包含一定数量的插槽

  • 插槽的数量限制了TaskManager能够执行的任务数量

  • 流程:

    1. 集群启动之后,TaskManager向资源管理器注册它的插槽

    2. 收到资源管理器指令后,TaskManager会将一个或多个插槽提供给JobManager使用

    3. JobManager就可以向插槽分配任务进行执行

    4. 执行过程中,一个TaskManager可以同其它运行同一程序的TaskManager交换数据

(4)分发器(Dispatcher)

分发器主要作用在当一个应用被提交执行时,分发器就会启动并将应用移交给一个JobManager,可以跨作业运行,为应用提交提供了REST接口。

  • 由于是REST接口,分发器可以作为集群的一个HTTP接入点,可以不受防火墙阻挡
  • 分发器也会启动一个WebUI,用来方便的展示和监控作业执行信息
  • 分发器在架构中并不是必须的,取决于提交运行的方式

02 任务提交流程

在01中,我们介绍了各个组件所承担的职责,那么在一个完整的作业提交过程中,它们是怎么协同配合完成作业提交的呢?

(1)高层级的视角

img

可以看到,作业提交大致分为五个步骤:

  • 提交作业
  • JobManager启动并向ResourceManager申请资源
  • ResourceManager分配Slot资源
  • TaskManager向JobManager提供slot
  • JobManager向slot提交执行任务

接着,我们以常用的yarn为例,看看在yarn中Flink任务是如何提交运行的。

(2) yarn集群中

img

  • 任务提交后,Client向HDFS上传Flink任务的jar包和配置
  • Client向Yarn ResourceManager提交任务
  • ResourceManager分配Container资源并通知对应的NodeManager启动ApplicationMaster
  • ApplicationMaster启动后加载Flink的jar包和配置构建环境,然后启动JobManager
  • ApplicationMaster向ResourceManager申请资源启动TaskManager
  • ResourceManager分配Container资源后,ApplicationMaster通知资源所在节点的NodeManager启动TaskManager
  • NodeManager加载Flink的jar包和配置构建环境启动TaskManager
  • TaskManager启动后向JobManager发送心跳包,并等待JobManager向其分配任务

03 任务调度原理

到此为止,相信你对Flink运行时的架构和作业运行流程有了清晰的认识,下面让我们再来看看其内部涉及到的一些原理。

在这里插入图片描述

首先,对运行时架构图做一下概念上的补充:

  • 客户端不是运行时和程序执行的一部分,用于准备并发送dataflow(JobGraph)给Master(JobManager),然后客户端断开连接或维持连接(根据作业提交方式)以等待接收结果
  • Flink集群启动后,首先会启动一个JobManager和一个或多个的TaskManager
  • Client提交任务给JobManager
  • JobManager再调度任务到各个TaskManager去执行
  • TaskManager将心跳和统计信息汇报给JobManager(Flink Standalone模式下)
  • TaskManager之间以流的形式进行数据传输
  • Client、JobManager、TaskManager均为独立的JVM进程

Client

  • 提交Job的客户端
  • 可以运行在任何机器上,与JobManager环境联通即可
  • 提交Job后,Client可以结束进程(Streaming的任务),也可以不结束并等待结果返回

JobManager

  • 负责调度Job并协调Task做checkpoint
  • 从client处接收到Job和Jar包等资源后,会生成优化后的执行计划
  • 并以Task的单元调度到各个TaskManager去执行

TaskManager

  • 在启动时设置好槽位数(slot),每个slot能启动一个Task,Task为线程
  • 从JobManager处接收需要部署的Task
  • 部署启动后,与自己的上游建立Netty连接,接收数据并处理

下面,让我来给你介绍下Flink作业在生产中的一些问题。

(1) TaskManager与Slot

Flink中每一个TaskManager都是一个JVM进程,它可能会在独立的线程上执行一个或多个subtask。

  • 通过task slot来控制每一个TaskManager能接收多少个task。
  • 每个task slot表示TaskManager拥有资源的一个固定大小的子集
  • 资源slot化意味着一个subtask将不需要跟来自其他job的subtask竞争被管理的内存
  • 通过调整task slot的数量,允许用户自定义subtask之间的隔离策略
    • 一个TaskManager一个task slot,则每个task group运行在独立的JVM中
    • 一个TaskManager多个task slot,多个subtask共享一个JVM,同一个JVM进程中,task将共享TCP连接(基于多路复用)和心跳信息,也可能共享数据集和数据结构,从而减少每个task的负载

下图中,每个TaskManager拥有3个slot。

img

子任务的算子可以共享slot,下图中,Source[1]、map[1]、keyBy().window().apply()[1]、sink[1]等子任务共享了第一个slot。

img

  • Flink允许子任务共享slot,即使它们是不同任务的子任务(前提来自同一个Job),这样一个slot可以保存作业的整个管道(即整个流式处理的过程)
  • Task Slot是静态的概念,指TaskManager具有的并发执行的能力,通过参数taskmanager.numberOfTaskSlots进行配置
  • 并行度parallelism是动态概念,即TaskMananger运行程序时实际使用的并发能力,通过参数parallelism.default进行配置

(2) 程序与数据流(dataflow)

img

Flink程序由三部分组成:Source、Transformation、Sink

  • Source负责读取数据源
  • Transformation利用各种算子进行处理加个
  • Sink负责输出

运行时,Flink上运行的程序会被映射成“逻辑数据流”(dataflows),每一个dataflow以一个或多个sources开始,以一个或多个sinks结束。

dataflow类似于任意的有向无环图(DAG),程序中的转换运算(Transformations)跟dataflow中的算子(operator)可能是一对一或一对多的关系

img

(3) 执行图(ExecutionGraph)

Flink程序直接映射成的数据流图是StreamGraph,即逻辑视图,表示的是计算逻辑的高级视图。
为了执行一个流处理程序,Flink需要将逻辑流图转换为物理数据流图(也叫执行图),执行图详细说明了程序的执行方式。

Flink的执行图可以分为四层:StreamGraph——》JobGraph——》ExecutionGraph——》物理执行图

  • StreamGraph:根据用户通过StreamAPI编写的代码生成的最初的图,用来表示程序的拓扑结构
  • JobGraph:StreamGraph经过优化后生成了JobGraph,提交给JobManager的数据结构
    • 主要的优化:
      • 将多个符合条件的节点chain在一起作为一个节点,从而减少数据在节点之间流动所需要的序列化、反序列化、传输消耗
  • ExecutionGraph:JobManager根据JobGraph生成ExecutionGraph,是JobGraph的并行化版本,是调度层最核心的数据结构
  • 物理执行图:JobMananger根据ExecutionGraph对Job进行调度后,在各个TaskManager上部署Task后形成的“图”,并不是一个具体的数据结构

下图详细展示了Flink执行图的衍生过程。

img

(4) 并行度(Parallelism)

Flink程序具有并行、分布式的特性。

执行过程中,一个流包含一个或多个分区,每一个算子可以包含一个或多个子任务,这些子任务在不同的线程、不同的物理机或不同的容器中彼此互不依赖的执行。

一个特定算子的子任务的个数被称之为并行度,一般把所有算子中最大的并行度称之为一个流程序的并行度;一个程序中,不同的算子可能具有不同的并行度。

如下图中,sink算子的并行度为1,map算子的并行度为2,整个程序的并行度为2。

img

Stream在算子之间传输数据的形式可以是one-to-one(forwarding)的模式,也可以是redistributing的模式。

  • One-to-One:Stream维护着分区以及元素的顺序。意味着map算子的子任务看到的元素的个数以及顺序跟source算子的子任务生产的元素的个数、顺序相同,map、fliter、flatMap等算子都是one-to-one的对应关系,类似于spark中窄依赖
  • Redistributing:stream的分区发生改变,每一个算子的子任务依据所选择的transformation发送数据到不同的目标任务,类似spark中宽依赖

(5) 任务链(Operator Chains)

在上文Flink执行图转换过程中,我们提到,从StreamGraph到JobGraph的过程中,会将符合条件的节点chain到一起,从而优化处理流程,这就是Operator Chains。

相同并行度的one-to-one操作,相连的算子链接在一起形成一个task,原来的算子成为里面的一部分

将算子链接成task是非常有效的优化:

  • 减少线程之间切换和基于缓存区的数据交换
  • 减少时延和提升吞吐量

下图中,key Agg算子和Sink算子被链接到了一起:

img

04 作业失败重启

Flink的作业失败大体可以分为两种原因:集群Master失败和Task执行失败。

(1) 集群Master失败

Flink 支持启动多个 Master 作为备份,这些 Master 可以通过 ZK 来进行选主,从而保证某一时刻只有一个Master 在运行。当前active的 Master 发生异常时 , 某个备份的 Master 可以接管协调的工作。

为了保证Master 可以准确维护作业的状态,Flink 目前采用了一种最简单的实现方式,即直接重启整个作业

(2) Task执行失败

对于Task执行失败的情况,我们需要逐个分析:

Restart-all

粗暴的直接重启所有Task,即从上一次的Checkpoint开始继续执行Task。

Restart-individual

对于各个Task之间不存在数据传输的情况,我们可以直接重启出错的Task。

Restart-Based

Flink 引入了一种新的 Region-Based 的 Failover 策略,用于处理批处理作业中没有Checkpoint导致的失败重启问题。

Flink 的批处理作业中,Task 之间有两种数据传输方式

  • Pipeline:上下游 Task 之间直接通过网络传输数据,需要上下游同时运行
  • Blocking:上游的 Task 会首先将数据进行缓存,上下游的 Task 可以单独执行

Flink 将 ExecutionGraph 中使用 Pipeline 方式传输数据的 Task 的子图叫做 Region,从而将整个ExecutionGraph 划分为多个子图。Region 内的 Task 必须同时重启,而不同 Region 的 Task 由于在 Region 边界存在 Blocking 的边,可以单独重启下游 Region 中的 Task

  • 如果是由于 Task 本身的问题发生错误,可以只重启该 Task 所属的 Region 中的 Task,这些 Task 重启之后,可以直接拉取上游 Region 缓存的输出结果继续进行计算
  • 如果错误是由于读取上游结果出现问题,如网络连接中断、缓存上游输出数据的 TaskExecutor 异常退出等,那么还需要重启上游 Region 来重新产生相应的数据。在这种情况下,如果上游 Region 输出的数据分发方式不是确定性 的(如 KeyBy、Broadcast 是确定性的分发方式,而 Rebalance、Random 则不 是,因为每次执行会产生不同的分发结果),为保证结果正确性,还需要同时重启上游 Region 所有的下游 Region

今天的分享就到这里了,如果感觉有所帮助,动动您的小手给我点个关注吧,感谢~
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程理想国

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值