10 分钟入门 Flink-- 架构和原理
相信你读完上一节的《10分钟入门Flink--了解Flink》对 Flink 已经有初步了解了。这是继第一节之后的 Flink 入门系列的第二篇,本篇主要内容是是:了解 Flink 运行模式、Flink 调度原理、Flink 分区、Flink 安装。
1、运行模式
Flink 有多种运行模式,可以运行在一台机器上,称为本地(单机)模式;也可以使用 YARN 作为底层资源调度系统以分布式的方式在集群中运行,称为 Flink On YARN 模式;还可以使用 Flink 自带的资源调度系统,不依赖其他系统,称为 Flink Standalone 模式。还有将 Flink 部署到 Kubernetes 的模式,称为 Flink On Kubernetes 模式。
1.1、单机(本地)模式
直接下载 jar 包后启动。
1.2、Flink Standalone 模式
先一起看下这种模式运行架构图:
Flink Standalone 模式为经典的主从(Master/Slave)架构,资源调度是 Flink 自己实现的。集群启动后,主节点上会启动一个 JobManager 进程,类似 YARN 集群的 ResourceManager,因此主节点也称为 JobManager 节点;各个从节点上会启动一个 TaskManager 进程,类似 YARN 集群的 NodeManager,因此从节点也称为 TaskManager 节点。
执行流程:Client 接收到 Flink 应用程序后,将作业提交给 JobManager。JobManager 要做的第一件事就是分配 Task(任务)所需的资源。完成资源分配后,Task 将被 JobManager 提交给相应的 TaskManager,TaskManager 会启动线程开始执行。在执行过程中,TaskManager 会持续向 JobManager 汇报状态信息,例如开始执行、进行中或完成等状态。作业执行完成后,结果将通过 JobManager 发送给 Client。
各组件作用:
Client:Client 是提交作业的客户端,虽然不是运行时和作业执行时的一部分,但它负责准备和提交作业到 JobManager,它可以运行在任何机器上,只要与 JobManager 环境连通即可。
JobManager:JobManager 根据客户端提交的应用将应用分解为子任务,从资源管理器(YARN 等)申请所需的计算资源,然后分发任务到 TaskManager 执行,并跟踪作业的执行状态等。JobManager 的主要作用是协调资源分配、任务调度、故障恢复等。整个集群有且仅有一个活跃的 JobManager。
TaskManager:TaskManager 是 Flink 集群的工作进程。Task 被调度到 TaskManager 上执行。TaskManager 相互通信,只为在后续的 Task 之间交换数据。TaskManager 工作内容:
接收 JobManager 分配的任务,负责具体的任务执行。TaskManager 会在同一个 JVM 进程内以多线程的方式执行任务
负责对应任务在每个节点上的资源申请,管理任务的启动、停止、销毁、异常恢复等生命周期。
负责对数据进行缓存。TaskManager 之间采用数据流的形式进行数据交互。
Task:Flink 中的每一个操作算子称为一个 Task(任务)。Task 是基本的工作单元,由 Flink 的 Runtime 来执行。每个 Task 在一个 JVM 线程中执行。多个 Task 可以在同一个 JVM 进程中共享 TCP 连接(通过多路复用技术)和心跳信息。它们还可能共享数据集和数据结构,从而降低每个 Task 的开销。
Task Slot:TaskManager 为了控制执行的 Task 数量,将计算资源(内存)划分为多个 Task Slot(任务槽),每个 Task Slot 代表 TaskManager 的一份固定内存资源,Task 则在 Task Slot 中执行。
1.3、Flink On YARN 模式
先来回顾下 yarn 的集群架构:
Flink On YARN 模式遵循 YARN 的官方规范,YARN 只负责资源的管理和调度,运行哪种应用程序由用户自己实现,因此可能在 YARN 上同时运行 MapReduce 程序、Spark 程序、Flink 程序等。
Flink On YARN 模式下的运行架构:
1.4、Flink On Kubernetes 模式
这种模式我没用过,不做介绍,感兴趣的朋友可以自行查阅。
2、调度原理
2.1、任务链
Flink 中的每一个操作算子称为一个 Task(任务),算子的每个具体实例则称为 SubTask(子任务),SubTask 是 Flink 中最小的处理单元,多个 SubTask 可能在不同的机器上执行。一个 TaskManager 进程包含一个或多个执行线程,用于执行 SubTask。TaskManager 中的一个 Task Slot 对应一个执行线程,一个执行线程可以执行一个或多个 SubTask。他们的结构如下图所示:
由于每个 SubTask 只能在一个线程中执行,为了能够减少线程间切换和缓冲的开销,在降低延迟的同时提高整体吞吐量,Flink 可以将多个连续的 SubTask 链接成一个 Task 在一个线程中执行。这种将多个 SubTask 连在一起的方式称为任务链。任务链的结构如下图所示:
2.2、并行度
Flink 应用程序可以在分布式集群上并行运行,其中每个算子的各个并行实例会在单独的线程中独立运行,并且通常情况下会在不同的机器上运行。为了充分利用计算资源,提高计算效率,可以增加算子的实例数(SubTask 数量)。一个特定算子的 SubTask 数量称为该算子的并行度,且任意两个算子的并行度之间是独立的,不同算子可能拥有不同的并行度。如下图所示,将 Source 算子、map()算子、keyby()/window()/apply()算子的并行度设置为 2,Sink 算子的并行度设置为 1。运行效果如下图所示:
2.3、数据流
一个 Flink 应用程序会被映射成逻辑数据流(Dataflow),而 Dataflow 都是以一个或多个 Source 开始、以一个或多个 Sink 结束的,且始终包括 Source、Transformation、Sink 三部分。
Dataflow 描述了数据如何在不同算子之间流动,将这些算子用带方向的直线连接起来会形成一个关于计算路径的有向无环图,称为 DAG(Directed Acyclic Graph,有向无环图)或 Dataflow 图。各个算子的中间数据会被保存在内存中。
Flink 算子组成的 DAG,如下图所示:
程序的 Dataflow 图,如下图所示:
2.3、执行图
Flink 应用程序执行时会根据数据流生成多种图,转成执行图,每种图对应了作业的不同阶段,根据不同图的生成顺序,主要分为 4 层:StreamGraph→JobGraph→ExecutionGraph→物理执行图。具体过程见下图:
StreamGraph:流图。使用 DataStream API 编写的应用程序生成的最初的图代表程序的拓扑结构,描述了程序的执行逻辑。StreamGraph 在 Flink 客户端中生成,在客户端应用程序最后调用 execute()方法时触发 StreamGraph 的构建。
JobGraph:作业图。所有高级别 API 都需要转换为 JobGraph。StreamGraph 经过优化(例如任务链)后生成了 JobGraph,以提高执行效率。StreamGraph 和 JobGraph 都是在本地客户端生成的数据结构,而 JobGraph 需要被提交给 JobManager 进行解析。
ExecutionGraph:执行图。JobManager 对 JobGraph 进行解析后生成的并行化执行图是调度层最核心的数据结构。它包含对每个中间数据集或数据流、每个并行任务以及它们之间的通信的描述。
物理执行图:JobManager 根据 ExecutionGraph 对作业进行调度后,在各个 TaskManager 上部署 Task 后形成的“图”。物理执行图并不是一个具体的数据结构,而是各个 Task 分布在不同的节点上所形成的物理上的关系表示。
2.4、执行计划
Flink 的优化器会根据数据量或集群机器数等的不同自动地为程序选择执行策略。
3、数据分区
3.1、分区概念
在 Flink 中,数据流或数据集被划分成多个独立的子集,这些子集分布到了不同的节点上,而每一个子集称为分区(Partition)。因此可以说,Flink 中的数据流或数据集是由若干个分区组成的。
3.2、分区数量
在运行期间,每个数据记录将被分配给一个或多个分区,各个分区中的数据可以并行计算。我们已经知道,数据是由上游算子的某个实例(SubTask)发往下游算子的一个或多个实例,而一个算子实例只负责计算一个分区的数据。因此,分区的数量是由下游算子的实例数量(并行度)决定的,发往下游算子的数据分区数量等于下游算子的实例数量。
数据分区的一个原则是使得分区的数量尽量等于集群节点 CPU 的核心数量。
3.3、分区策略
Flink 分区策略决定了一条数据如何发送给下游算子的不同实例。
转发策略
广播策略
键值策略
随机策略
全局策略
自定义策略
如果内置的分区策略不能满足当前需求,则可以在程序中自定义分区策略。这块后续再介绍。
4、安装
具体安装步骤参见下一篇。文中会介绍 Flink Standalone 的集群安装。
评论