【3】FLink 运行架构与资源分配

272 阅读5分钟

相关资料可以参考:nightlies.apache.org/flink/flink…

整体运行架构

Flink 运行整体由三部分组成:Client + JobManager + TaskManager。 如下图所示:

image.png

Client

Client 负责 flink 任务的提交,负责 flink 运行图的构建,在 yarn 上运行的 Session 模式和 Per-Job 模式下,client 会执行程序的 main 方法,构建执行图,在 application 模式下,则会把构件图和执行 main 方法放在 JobManager 中。

Client 本身不参与任务的运行,一般提交完任务后,client 就终止了,在附加模式下,client 可以等待任务执行结果。

JobManager

顾名思义,JobManager 是用来管理整个 Job 的,flink 任务的运行类似 master-slave 架构,而 JobManager 就是其中的 master,JobManager 可以有多个,但是只有一个在运行,其他的 standby。

JobManager 主要由三个内部组件组成:

  1. ResourceManager ResourceManager 用于分配 task slots 使用,负责资源的分配,当 flink 任务在不同集群上运行时,使用的 ResourceManager 不一样,比如 KubernetesResourceManager、YarnResourceManager及MesosResourceManager。ResourceManager 只分配资源,不执行任务。

其中 KubernetesResourceManager、YarnResourceManager 等支持动态分配资源,而 StandaloneResourceManager 是静态资源管理。

在 ResourceManager 中,最核心的是 ResourceManagerRuntimeServices,其包含 JobLeaderIdService、SlotManager 和 HeartbeatServices 等信息,并将这些信息注册到 ResourceManager 中。

  • JobLeaderIdService:实时监听和管理 JobManager 的各个节点的信息,JobManager 切换主节点行为、启动 leader 选举等
  • SlotManager:管理集群的 slot 资源,并注册 taskmanager 信息
  • HeartbeatServices:收集来自 taskmanager 和 jobmanager 的心跳信息,让其保持连接
  1. Dispatcher 提供一个 RPC 接口,用于提交作业,并为每个每个执行的作业启动一个新的 JobMaster,除此之外,其还运行 Flink Web UI。

其作业提交流程为(参考: blog.csdn.net/lisenyeahye…

image.png

  1. JobMaster JobMaster 由 Dispatcher 启动,主要是负责单个的 JobGraph 的运行,JobMaster 会先将 JobGraph 转换为可执行的 ExecutionGraph,并提交到 taskmanager 去运行。

一个作业对应一个 JobMaster,一个 flink 集群可以有多个作业。

关于 flink 中的各种图的解释

在 flink 中,经常可见 StreamGraph、JobGraph、ExecutionGraph、PhysicalGraph 等,下面一一解释。

  • StreamGraph:flink 根据用户使用 Stream API 编写的程序所生成的最初的逻辑图,该图一般在执行 main 方法的时候生成。
  • JobGraph:flink 根据 StreamGraph 进行优化后的图,比如有些步骤可以合并为一个步骤(算子链合并),减少数据传输等开支,client 提交任务的时候提交的是 JobGraph。
  • ExecutionGraph:根据设置的并行度等,将 JobGraph 进行并行化处理,预先分配好如何并行运行,生成并行图,调度的时候按照 ExecutionGraph 的节点进行调度。
  • PhysicalGraph:根据 ExecutionGraph 及资源分配情况,在 TaskManager 上实际运行的图。

这几种图用图形化表示示例:(参考:blog.csdn.net/u011047968/…

image.png

TaskManager

TaskManager 就是实际运行 Task 的节点,相当于 master-slave 架构的 slave 节点。数据算子的实际运行在 TaskManager 上运行,TaskManager 又叫 worker,是一个独立的 JVM 进程。

在 JobMaster 将任务提交给 TaskManager 之后,TaskManager 启动一个线程运行任务。JobMaster 是通过 RPC 来提交任务的。任务启动代码在 TaskExecutor#submitTask 方法中。

在任务启动的时候,TaskManager 主要做以下事情:

  • 与 JobManager 建立连接
  • 标记分类的 slot 为活动资源
  • 读取持久化存储的 job 和 task 信息
  • 将该 task 注册到 metrics group(后面会讲)
  • 构造 InputSplitProvider(后面会讲)
  • 构造 TaskOperatorEventGateway(后面会讲)
  • 从 JobManager 获取其他的重要信息,比如:task 状态上报器,checkpoint 触发器,task 计数器,classloader 获取等等
  • 构造 task 的状态管理器,状态管理存储等
  • 构造 Task 对象并将 Task 加入 slot 并启动线程,Task 继承自 Runnable,是一个单线程(在其他资料里面,Task 一般称为 SubTask,这里说的 Task 是 org.apache.flink.runtime.taskmanager.Task 对象,就是 SubTask)

Task 是最终在 TaskManager 中实际运行的线程,一个 Task 代表一个线程,这个线程中可以运行多个算子(算子链合并,可以禁用),一个 TaskManager 中可以运行多个 Task。而一个 slot 中一般只会运行一个 Task,但是有 slot 共享机制让一个 slot 中可以运行多个 Task。

一个 slot 表示一块独立划分的资源,主要表示内存资源。slot 可以复用共享给多个 Task。

由此我们得到各个概念之间的数量对应关系。

  • JobManager:TaskManager = 1:N
  • TaskManager:Slot = 1:N
  • TaskManager:Task = 1:N
  • Slot:Task = 1:1 (共享的情况下 1:N)

在使用 flink 的过程中,还会碰到一个常见的概念,即「并行度」,并行度表示某个算子的最大处理线程数,比如对输入数字进行求和的算子,并行度是 4 表示有 4 个线程在同时求和。

结合上面说的 Task 是一个单线程的概念,即并行度表示某个算子实际运行的 Task 的数量。一个作业有很多算子,不同的算子之间可能有不同的并行度。

综合上面的概念,再参考下面的图,我们可以总结一下。

image.png

如图所示:

  • 该 Job 有一个 JobManager(图中未画出)和 2 个 TaskManager
  • 该 Job 一共分配了 6 个 slot,每个 TaskManager 有 3 个 slot
  • Source 和 map 算子发生了算子链合并,合并成了一个 Task,一共有 6 个 Task 即 6 个线程在运行 Source + map,其并行度为 6
  • keyBy + window + apply 发生了算子链合并,合并成了一个 Task,一共有 6 个该 Task 在运行,并行度也为 6
  • 只有一个 sink 的 Task,并行度为 1
  • 一个 slot 中可以运行多个 Task,它们之间共享内存资源
  • 一个 slot 中不能运行两个一样的 Task,由于并行度是 6,所以 slot 最少应该为 6 个

Flink 节点通信

Flink 的 JobManager 和 TaskManager 之间进行通信使用的是 Akka 库进行通信,底层使用 actor 通信模型来实现,使用 RPC 进行通信。关于 Akka 请自行学习即可(可参考:Akka - 秒懂 - 疯狂创客圈 - 博客园 (cnblogs.com))。

在运行过程中,不同的 TaskManager 之间可能需要经常交换数据,这部分数据主要使用 Netty 来进行数据交换。