Flink 架构初探

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 上运行的模式

  1. 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
  1. 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)

你可能感兴趣的:(Flink 架构初探)