• Flink 运行时的组件
  • 任务提交流程
  • 任务调度原理
    • Yarn 提交流程
  • 任务调度
  • Work 与 Slots

Flink 运行时组件

作业管理器(JobManager)

  • 控制一个应用程序的主进程,也就是说,每一个应用程序都会被一个不同的JobManager 所控制执行 ;
  • JobManager 会先接收要执行的程序,这个应用程序包括:作业图(JobGraph)、逻辑数据流图(logical dataflow graph)和打包了所有类、库和其他资源的 JAR 包;
  • JobManager 会把 JobGraph 转换成一个物理层面的数据流图,这个图被叫做“执行图”(ExecutionGraph),包含了所有可以并发的任务;
  • JobManager 回向资源管理器(ResourceManager)请求执行必要资源,也就是任务管理器(TaskManager)上的插槽(Slot),一旦他获取到了足够的资源,就会将执行图分发到真正运行他们的 TaskManager 上,而在运行过程中,JobManager 会负责所有需要中央协调的操作。比如说检查点(Checkpoints)的协调。

任务管理器(TaskManager)

  • Flink 中的工作进程。一个Flink 任务会有多个 TaskManager 运行,一个TaskManager 会包含一定数量的 slots . Slots 的数量限制了能够执行任务的数量;
  • TaskManager 会在启动之后向资源管理器注册他的 Slots . 收到资源管理器的指令后,TaskManager 就会将一个 或 多个 Slots 提供给 JobManager 调用。 JobManager 就可以向 Slots 分配 Task 任务来执行;
  • 在执行的过程中,一个 TaskManager 可以跟其他的运行同一应用程序的TaskManager 交换数据。

资源管理器(ResourceManager)

  • 主要负责管理 任务管理器(TaskManager)的 slots . slot 是 Flink中的处理资源单元;
  • Flink为不同的环境和资源管理工具提供了不同的资源管理器。比如说 Yarn , Mesos , K8s 以及 Standalone 的部署。
  • 当 JobManager 申请 资源时, ResourceManager 会将有空闲 slots 的 TaskManager 分配给 JobManager . 如果 ResourceManager 没有足够的 slots 来满足 JobManager 的需要,它还可以向 资源提供平台 发起会话,以提供启动 TaskManager 进程的容器。

分发器(Dispacher)

  • 可以跨作业调度,它为应用提交提供了 REST 接口 ;
  • 当一个应用被提交执行时,分发器就会启动并将应用移交给一个 JobManager ;
  • Dispatcher 也会启动一个 WebUI , 用来方便展示和监控作业执行的信息;
  • Dispatcher 在架构中可能并不是必须的,这取决于应用的提交方式。

任务提交流程

任务提交流程

Yarn 任务提交流程

z
Yarn 任务调度

Flink任务提交后,Client向HDFS上传Flink的Jar包和配置,之后向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向其分配任务。

任务调度

任务调度

客户端不是运行时和程序执行的一部分,但它用于准备并发送dataflow(JobGraph)给Master(JobManager),然后,客户端断开连接或者维持连接以等待接收计算结果。

当 Flink集群启动后,首先会启动一个 JobManger和一个或多个的 TaskManager。由 Client提交任务给 JobManager,JobManager再调度任务到各个 TaskManager去执行,然后 TaskManager 将心跳和统计信息汇报给 JobManager。TaskManager之间以流的形式进行数据的传输。上述三者均为独立的 JVM 进程。

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

JobManager 主要负责调度 Job并协调 Task做 checkpoint,职责上很像 Storm的 Nimbus。从 Client处接收到 Job和 JAR包等资源后,会生成优化后的执行计划,并以 Task 的单元调度到各个 TaskManager去执行。

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

关于执行图

Flink 中的执行图可以分成四层:

  • StreamGraph
  • JobGraph
  • ExecutionGraph
  • 物理执行图。

StreamGraph:是根据用户通过 Stream API 编写的代码生成的最初的图。用来表示程序的拓扑结构。

JobGraph:StreamGraph经过优化后生成了 JobGraph,提交给 JobManager 的数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。

ExecutionGraph:JobManager 根据 JobGraph 生成ExecutionGraph。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。

物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。

关于执行图

Worker与Slots

每一个worker(TaskManager)都是一个JVM进程**,它可能会在独立的线程上执行一个或多个subtask。为了控制一个worker能接收多少个task,worker通过task slot来进行控制(一个worker至少有一个task slot)。

每个task slot表示TaskManager拥有资源的一个固定大小的子集。假如一个TaskManager有三个slot,那么它会将其管理的内存分成三份给各个slot。资源slot化意味着一个subtask将不需要跟来自其他job的subtask竞争被管理的内存,取而代之的是它将拥有一定数量的内存储备。需要注意的是,这里不会涉及到CPU的隔离,slot目前仅仅用来隔离task的受管理的内存。

通过调整task slot的数量,允许用户定义subtask之间如何互相隔离。如果一个TaskManager一个slot,那将意味着每个task group运行在独立的JVM中(该JVM可能是通过一个特定的容器启动的),而一个TaskManager多个slot意味着更多的subtask可以共享同一个JVM。而在同一个JVM进程中的task将共享TCP连接(基于多路复用)和心跳消息。它们也可能共享数据集和数据结构,因此这减少了每个task的负载。

img

Task Slot 是静态的概念,是指TaskManager具有的并发执行能力,可以通过参数taskmanager.numberOfTaskSlots进行配置;而并行度parallelism是动态概念,即TaskManager运行程序时实际使用的并发能力,可以通过参数parallelism.default进行配置。

也就是说,假设一共有3个TaskManager,每一个TaskManager中的分配3个TaskSlot,也就是每个TaskManager可以接收3个task,一共9个TaskSlot,如果我们设置parallelism.default=1,即运行程序默认的并行度为1,9个TaskSlot只用了1个,有8个空闲,因此,设置合适的并行度才能提高效率。

程序与数据流

img

所有的Flink程序都是由三部分组成的: SourceTransformationSink

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

在运行时,Flink上运行的程序会被映射成streaming dataflows,它包含了这三部分。每一个dataflow以一个或多个sources开始以一个或多个sinks结束。dataflow类似于任意的有向无环图(DAG),当然特定形式的环可以通过iteration构建。在大部分情况下,程序中的transformations跟dataflow中的operator是一一对应的关系,但有时候,一个transformation可能对应多个operator。

img

并行数据流

Flink程序的执行具有并行、分布式的特性。在执行过程中,一个 stream 包含一个或多个 stream partition ,而每一个 operator 包含一个或多个 operator subtask,这些operator subtasks在不同的线程、不同的物理机或不同的容器中彼此互不依赖得执行。

一个特定operator的subtask的个数被称之为其parallelism(并行度)。一个stream的并行度总是等同于其producing operator的并行度。一个程序中,不同的operator可能具有不同的并行度。

img

Stream在operator之间传输数据的形式可以是one-to-one(forwarding)的模式也可以是redistributing的模式,具体是哪一种形式,取决于operator的种类。

One-to-one:stream(比如在source和map operator之间)维护着分区以及元素的顺序。那意味着map operator的subtask看到的元素的个数以及顺序跟source operator的subtask生产的元素的个数、顺序相同,map、fliter、flatMap等算子都是one-to-one的对应关系。

Ø 类似于spark中的窄依赖

Redistributing:stream(map()跟keyBy/window之间或者keyBy/window跟sink之间)的分区会发生改变。每一个operator subtask依据所选择的transformation发送数据到不同的目标subtask。例如,keyBy() 基于hashCode重分区、broadcast和rebalance会随机重新分区,这些算子都会引起redistribute过程,而redistribute过程就类似于Spark中的shuffle过程。

Ø 类似于spark中的宽依赖

4.6 task与operator chains

相同并行度的one to one操作,Flink这样相连的operator 链接在一起形成一个task,原来的operator成为里面的subtask。将operators链接成task是非常有效的优化:它能减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。链接的行为可以在编程API中进行指定。

img