1. Overview
本文将从 Flink 的基本概念入手,了解 Flink 的设计理念、运行架构以及任务提交和调度的流程。旨在对 Flink 从整体上建立一个初步的认识。
2. Basic
2.1 JobManager
The Flink runtime consists of two types of processes: a JobManager and one or more TaskManagers.
Flink 运行时有两类重要进程,JobManager 和 TaskManager。其中 JobManager 负责协调整个分布式程序的运行,比如任务调度、任务失败恢复、资源的调度等等。JobManager 有三个重要组件:
-
ResourceManager:
负责管理集群中的资源:Task slot,Flink 实现了多种 ResourceManager 来适配不同的资源容器如 Yarn、K8s 等。
-
Dispatcher:
提供一个 Restful 的 http 接口来负责 Flink 程序的提交以及为 WebUI 提供所需要的信息。
-
JobMaster:
负责管理 job 的执行。多个 job 可以同时运行在 Flink 集群中,每一个 job 都有对应的 JobMaster 管理。
2.2 TaskManager
TaskManager 是 Flink 集群的工作进程。Task 被调度到 TaskManager 上执行。TaskManager 之间可以相互通信方便后续的 Task 之间交换数据。
2.3 Task & Sub-Task
Task 是 Physical Graph 的节点。它是基本的工作单元。Task 封装了 Operator 或者 Operator Chain 的多个并行实例。
Sub-Task 强调的是同一个 Operator 或者 Operator Chain 的多个并行的 Task 。
2.4 JobGraph/LogicalGraph/DataflowGraph
提交 Flink Application 时创建而成的节点是 Operator 或者 Operator Chain 组成的 DAG 图。
2.5 ExecutionGraph/PhysicalGraph
根据 LogicalGraph 生成的节点是 Task 组成的 DAG 图,可以理解为 LogicalGraph 的并行化版本,最终在 TaskManager 上部署。
2.6 Task Slot & parallelism
每个 worker(TaskManager) 都是一个 JVM 进程。在这个进程中,可以为每个运行的 subTask 分配单独的线程。他们运行在 Task Slot 中。每个 Task Slot 表示的是 TaskManager 固定的资源(特指内存资源,不包含 CPU)。比如一个 TaskManager 有 3 个 Task Slot,那么每个 Slot 将管理该节点的 1/3 的内存。Task Slot 是对一个 TaskManager 内存资源分割方式的静态描述,并不代表某个算子实际运行的 subTask 个数。设置实际运行的 subTask 个数由 parallelism(并行度) 负责。不同的算子可以有不同的并行度。
2.7 优化设计
Flink 为了节省内存开销和网络 I/O 成本,进行了两个重要的优化。分别是 Operator Chains 和 Slot Sharing。
2.7.1 Operator Chains
对于连续多个没有 repartition 的算子,Flink 默认会将他们合并成一个 Operator Chains(类似于 Spark 中的 Stage),运行在一个线程,即一个 subTask 中。这样做的好处是减少了多个线程之间切换的开销,减少了延迟也就增加了集群处理数据的吞吐量。
6
2.7.2 Slot Sharing
Operator Chains 减少了多个线程之间的切换开销,现在集群运行时架构如下图:
这里引入一个概念:pipeline,可以简单的理解为数据流向。上图中每个 TaskManager 的 pipeline 是从左到右的。对于 Source/map 这种不太需要内存资源的算子,他们和 window 这种需要内存资源的算子分到的内存资源是一样的,这就导致集群的资源利用率不高。Flink 为了解决这个问题,引入了 Slot Sharing 的概念。默认情况下,只要是同一个 job 的 task 都可以共享一个 slot。引入该机制后,集群运行时的架构如下图:
之前是一个 TaskManager 有一个 pipline,此时每个 slot 都可以有一个完整的 pipline。pipline 的流向变成了从上到下。这样对于每一个 slot,便不会有内存资源比较闲置的时候,从而提高了资源的利用率。另外一个好处是计算一个 job 需要的 slot,只取决于最大的并发度,不需要关注并发度在算子间的变化过程。
3. Deep
3.1 Deployment
以下列举几种常见的部署方式
3.1.1 Standalone 模式
- 启动集群:
修改 /conf/slaves 文件然后将 flink 分发给 slaves 节点。通过start-cluster.sh
启动集群。启动后可以通过 jps 看到 JobManager 进程名为 StandaloneSessionClusterEntrypoint ,TaskManager 进程名为 TaskManagerRunner。 - 提交任务:
如果是从本地读取数据,则需要将数据文件分发到各个 taskmanager 节点。执行命令:
./flink run -c com.test.WordCount -p 2 FlinkWordCount.jar
其中 -c 指定主类,-p 指定并行度(parallelism)
3.1.2 Yarn 模式
Flink 提供了两种在 yarn 上运行的模式
- Session-Cluster 模式:
该模式需要先启动集群,再提交作业。各个作业共享/竞争该集群的资源,共享 Dispatcher 和 ResourceManager。适合规模小执行时间短的作业。
- 启动 hadoop 集群( yarn,hdfs )
- 启动 yarn-session:
./yarn-session.sh -n 2 -s 2 -jm 1024 -tm 1024 -nm wordCountTest -d
其中:
-n(--container):TaskManager 的数量
-s(--slots): 每个 TaskManager 的 slot 数量
-jm:JobManager 的内存(MB)
-tm:每个 taskmanager 的内存(MB)
-nm:yarn 的 appName
-d:后台执行 - 执行任务:
./flink run -c com.test.WordCount FlinkWordCount.jar
- Per-Job-Cluster 模式:
一个 Job 对应一个集群,每提交一个作业,会单独向 yarn 申请资源。各个作业独享 DIspatcher 和 ResourceManager。适合规模大长时间运行的作业。
- 启动 hadoop 集群( yarn,hdfs )
- 不启动 yarn-session,直接执行任务
./flink run -m yarn-cluster -c com.test.WordCount FlinkWordCount.jar
3.2 Setting slot & parallelism
3.2.1 slot 和 parallelism 区别
1.slot是静态的概念,是指taskmanager具有的并发执行能力
2.parallelism是动态的概念,是指程序运行时实际使用的并发能力
3.设置合适的parallelism能提高运算效率,太多了和太少了都不行
3.2.2 set slot
- 修改 $FLINK_HOME/conf/flink-conf.yaml 文件中的 taskmanager.numberOfTaskSlots 属性
- 启动 yarn session 时通过 -s 指定
3.2.3 set parallelism
- 修改 $FLINK_HOME/conf/flink-conf.yaml 文件中parallelism.default 属性
- 提交 job 时通过 -p 参数
- 程序中通过
env.setParallelism(3)
指定 - 对每个算子单独指定:
val env =ExecutionEnvironment.getExecutionEnvironment
env.setParallelism(2)
val data = env.fromElements(1,2,3,4)
data.map(x=>x*2).setParallelism(4)