当前位置: 首页 > news >正文

Flink 运行时架构(三)

目录

4.1 系统架构

4.1.1 整体构成

4.1.2 作业管理器(JobManager)

4.1.3 任务管理器(TaskManager)

4.2 作业提交流程

4.2.1 高层级抽象视角

4.2.2 独立模式(Standalone)

4.2.3 YARN 集群  

4.3 一些重要概念

4.3.1 数据流图( Dataflow Graph)

4.3.2 并行度( Parallelism)

4.3.3 算子链(Operator Chain)

4.3.4 作业图(JobGraph)与执行图( ExecutionGraph)

 4.3.5 任务(Tasks)和任务槽(Task Slots)


4.1 系统架构

4.1.1 整体构成

Flink 的运行时架构中,最重要的就是两大组件:作业管理器(JobManger)任务管理器 (TaskManager)。对于一个提交执行的作业, JobManager 是真正意义上的“管理者”(Master), 负责管理调度,所以在不考虑高可用的情况下只能有一个;而 TaskManager 是“工作者” (Worker、 Slave),负责执行任务处理数据,所以可以有一个或多个。

说明:

其实客户端并不是处理系统的一部分,它只负责作业的 提交。具体来说,就是调用程序的 main 方法,将代码转换成“数据流图”(Dataflow Graph), 并最终生成作业图(JobGraph),一并发送给 JobManager。提交之后,任务的执行其实就跟客 户端没有关系了;我们可以在客户端选择断开与 JobManager 的连接, 也可以继续保持连接。 之前我们在命令提交作业时,加上的-d 参数,就是表示分离模式(detached mode),也就是断 开连接。

当然,客户端可以随时连接到 JobManager,获取当前作业的状态和执行结果,也可以发 送请求取消作业。我们在上一章中不论通过 Web UI 还是命令行执行“flink run”的相关操作, 都是通过客户端实现的。

JobManager 和 TaskManagers 可以以不同的方式启动:

⚫ 作为独立(Standalone)集群的进程,直接在机器上启动

⚫ 在容器中启动

⚫ 由资源管理平台调度启动,比如 YARN、 K8S

这其实就对应着不同的部署方式。

4.1.2 作业管理器(JobManager)

概念:

JobManager 是一个 Flink 集群中任务管理和调度的核心,是控制应用执行的主进程。也就 是说,每个应用都应该被唯一的 JobManager 所控制执行。当然,在高可用(HA)的场景下,可能会出现多个 JobManager;这时只有一个是正在运行的领导节点(leader),其他都是备用 节点(standby)。

JobManager的三个组件:

 一:JobMaster

JobMaster 是 JobManager 中最核心的组件,负责处理单独的作业(Job)。所以 JobMaster

和具体的 Job 是一一对应的,多个 Job 可以同时运行在一个 Flink 集群中, 每个 Job 都有一个 自己的 JobMaster。需要注意在早期版本的 Flink 中,没有 JobMaster 的概念;而 JobManager

的概念范围较小,实际指的就是现在所说的 JobMaster。

JobMaster的功能:

在作业提交时, JobMaster 会先接收到要执行的应用。这里所说“应用”一般是客户端提交来的,包括: Jar 包,数据流图(dataflow graph),和作业图(JobGraph)。 JobMaster 会把 JobGraph 转换成一个物理层面的数据流图,这个图被叫作“执行图” ( ExecutionGraph),它包含了所有可以并发执行的任务。 JobMaster 会向资源管理器 (ResourceManager)发出请求,申请执行任务必要的资源。一旦它获取到了足够的资源,就会 将执行图分发到真正运行它们的 TaskManager 上。而在运行过程中, JobMaster 会负责所有需要中央协调的操作,比如说检查点(checkpoints) 的协调。

二:资源管理器(ResourceManager)

ResourceManager 主要负责资源的分配和管理,在 Flink 集群中只有一个。所谓“资源”, 主要是指 TaskManager 的任务槽(task slots)。任务槽就是 Flink 集群中的资源调配单元,包含了机器用来执行计算的一组 CPU 和内存资源。每一个任务(Task)都需要分配到一个 slot 上 执行。

Flink 的 ResourceManager,针对不同的环境和资源管理平台(比如 Standalone 部署,或者

YARN),有不同的具体实现。在 Standalone 部署时,因为 TaskManager 是单独启动的(没有

Per-Job 模式),所以 ResourceManager 只能分发可用 TaskManager 的任务槽,不能单独启动新TaskManager

而在有资源管理平台(YARN模式下)时,就不受此限制。当新的作业申请资源时, ResourceManager 会将 有空闲槽位的 TaskManager 分配给 JobMaster。如果 ResourceManager 没有足够的任务槽,它 还可以向资源提供平台发起会话,请求提供启动 TaskManager 进程的容器。另外,ResourceManager 还负责停掉空闲的 TaskManager,释放计算资源。

三:分发器(Dispatcher)

Dispatcher 主要负责提供一个 REST 接口,用来提交应用,并且负责为每一个新提交的作业启动一个新的 JobMaster 组件。 Dispatcher 也会启动一个 Web UI,用来方便地展示和监控作业执行的信息。 Dispatcher 在架构中并不是必需的,在不同的部署模式下可能会被忽略掉。

4.1.3 任务管理器(TaskManager)

TaskManager 是 Flink 中的工作进程,数据流的具体计算就是它来做的,所以也被称为 “Worker”。 Flink 集群中必须至少有一个 TaskManager;当然由于分布式计算的考虑,通常会有多个 TaskManager 运行,每一个 TaskManager 都包含了一定数量的任务槽(task slots)。 Slot是资源调度的最小单位,slot 的数量限制了 TaskManager 能够并行处理的任务数量。启动之后,TaskManager 会向资源管理器注册它的 slots;收到资源管理器的指令后,TaskManager 就会将一个或者多个槽位提供给 JobMaster 调用,JobMaster 就可以分配任务来执行了。在执行过程中, TaskManager 可以缓冲数据,还可以跟其他运行同一应用的 TaskManager交换数据

4.2 作业提交流程

4.2.1 高层级抽象视角

( 1) 一般情况下,由客户端( App)通过分发器提供的 REST 接口,将作业提交给JobManager。

(2)由分发器启动 JobMaster,并将作业(包含 JobGraph)提交给 JobMaster。

(3) JobMaster 将 JobGraph 解析为可执行的 ExecutionGraph,得到所需的资源数量,然后 向资源管理器请求资源(slots)。

(4)资源管理器判断当前是否由足够的可用资源;如果没有,启动新的 TaskManager。

(5) TaskManager 启动之后,向 ResourceManager 注册自己的可用任务槽(slots)。

(6)资源管理器通知 TaskManager 为新的作业提供 slots。

(7) TaskManager 连接到对应的 JobMaster,提供 slots。

(8) JobMaster 将需要执行的任务分发给 TaskManager。

(9) TaskManager 执行任务,互相之间可以交换数据。

说明:不同部署环境下的提交流程是不同的。接下来我们就具体介绍一下不同部署环境下的提交流程。

4.2.2 独立模式(Standalone)

在独立模式( Standalone)下,只有会话模式和应用模式两种部署方式。两者整体来看流 程是非常相似的: TaskManager 都需要手动启动,所以当 ResourceManager 收到 JobMaster 的请求时,会直接要求 TaskManager 提供资源。而 JobMaster 的启动时间点,会话模式是预先启动,应用模式则是在作业提交时启动

上图是StandAlone 集群作业提交流程

4.2.3 YARN 集群  

在有资源管理器的环境下,以 YARN 为例,分不同的部署模式来做具体说明。

1 . 会话(Session)模式

在会话模式下,我们需要先启动一个 YARN session,这个会话会创建一个 Flink 集群。

这里只启动了 JobManager,而 TaskManager 可以根据需要动态地启动。在 JobManager 内部,由于还没有提交作业,所以只有 ResourceManager 和 Dispatcher 在运行 

接下来就是真正提交作业的流程,如上图所示。

( 1)客户端通过 REST 接口,将作业提交给分发器。

(2)分发器启动 JobMaster,并将作业(包含 JobGraph)提交给 JobMaster。

(3) JobMaster 向资源管理器请求资源(slots)。

(4)资源管理器向 YARN 的资源管理器请求 container 资源。

(5) YARN 启动新的 TaskManager 容器。

(6) TaskManager 启动之后,向 Flink 的资源管理器注册自己的可用任务槽。

(7)资源管理器通知 TaskManager 为新的作业提供 slots。

( 8) TaskManager 连接到对应的 JobMaster,提供 slots。

(9) JobMaster 将需要执行的任务分发给 TaskManager,执行任务。

可见,整个流程除了请求资源时要“上报” YARN 的资源管理器,其他与 4.2.1 节所述抽象流程几乎完全一样。

2. 单作业(Per-Job)模式

在单作业模式下, Flink 集群不会预先启动,而是在提交作业时,才启动新的 JobManager。

( 1)客户端将作业提交给 YARN 的资源管理器,这一步中会同时将 Flink 的 Jar 包和配置 上传到 HDFS,以便后续启动 Flink 相关组件的容器。

(2) YARN 的资源管理器分配 Container 资源,启动 Flink JobManager,并将作业提交给

JobMaster。这里省略了 Dispatcher 组件。

(3) JobMaster 向资源管理器请求资源(slots)。

(4)资源管理器向 YARN 的资源管理器请求 container 资源。

(5) YARN 启动新的 TaskManager 容器。

(6) TaskManager 启动之后,向 Flink 的资源管理器注册自己的可用任务槽。

(7)资源管理器通知 TaskManager 为新的作业提供 slots。

( 8) TaskManager 连接到对应的 JobMaster,提供 slots。

(9) JobMaster 将需要执行的任务分发给 TaskManager,执行任务。

可见,区别只在于 JobManager 的启动方式,以及省去了分发器。当第 2 步作业提交给

JobMaster,之后的流程就与会话模式完全一样了。

 3. 应用(Application)模式

应用模式与单作业模式的提交流程非常相似,只是初始提交给 YARN 资源管理器的不再 是具体的作业,而是整个应用。一个应用中可能包含了多个作业,这些作业都将在 Flink 集群 中启动各自对应的 JobMaster。

4.3 一些重要概念

4.3.1 数据流图( Dataflow Graph)

Flink 是流式计算框架。它的程序结构,其实就是定义了一连串的处理操作,每一个数据 输入之后都会依次调用每一步计算。在 Flink 代码中,我们定义的每一个处理转换操作都叫作“算子”(Operator),所以我们的程序可以看作是一串算子构成的管道,数据则像水流一样有序 地流过。比如在之前的 WordCount 代码中,基于执行环境调用的 socketTextStream()方法,就是一个读取文本流的算子;而后面的 flatMap()方法,则是将字符串数据进行分词、转换成二元组的算子。

所有的 Flink 程序都可以归纳为由三部分构成: Source、 Transformation 和 Sink。

Source 表示“源算子”,负责读取数据源

Transformation 表示“转换算子”,利用各种算子进行处理加工

Sink 表示“下沉算子”,负责数据的输出

在运行时, Flink 程序会被映射成所有算子按照逻辑顺序连接在一起的一张图,这被称为“逻辑数据流”(logical dataflow),或者叫“数据流图”(dataflow graph)。

4.3.2 并行度( Parallelism)

1 . 什么是并行计算

因为数据是连续不断到来的,我们完全可以按照数据流 图建立一个“流水线”,前一个操作处理完成,就发往处理下一步操作的节点。如果说 Spark基于 MapReduce 架构的思想是“数据不动代码动”,那么 Flink 就类似“代码不动数据流动”, 原因就在于流式数据本身是连续到来的、我们不会同时传输所有数据,这其实是更符合数据流 本身特点的处理方式。

在大数据场景下,我们都是依靠分布式架构做并行计算,从而提高数据吞吐量的。既然处 理完一个操作就可以把数据发往别处,那我们就可以将不同的算子操作任务,分配到不同的节 点上执行了。

但是算子之间是有执行顺序的,对一 条数据来说必须依次执行;而一个算子在同一时刻只能处理一个数据。比如之前 WordCount, 一条数据到来之后,我们必须先用 source 算子读进来、再做 flatMap 转换;一条数据被 source读入的同时,之前的数据可能正在被 flatMap 处理,这样不同的算子任务是并行的。但如果多 条数据同时到来,一个算子是没有办法同时处理的,我们还是需要等待一条数据处理完、再处理下一条数据——这并没有真正提高吞吐量。

2. 并行子任务和并行度

这么实现并行呢?

我们把一个算子操作,“复制”多份到多个节点, 数据来了之后就可以到其中任意一个执行。这样一来,一个算子任务就被拆分成了多个并行的 “子任务”(subtasks),再将它们分发到不同节点,就真正实现了并行计算。

在 Flink 执行过程中,每一个算子(operator)可以包含一个或多个子任务(operator subtask), 这些子任务在不同的线程、不同的物理机或不同的容器中完全独立地执行。

一个特定算子的子任务(subtask)的个数被称之为其并行度(parallelism)。这样,包含并 行子任务的数据流,就是并行数据流,它需要多个分区(stream partition)来分配并行任务。 一般情况下,一个流程序的并行度,可以认为就是其所有算子中最大的并行度。一个程序中,不同的算子可能具有不同的并行度。

当前数据流中有 source、 map、 window、 sink 四个算子,除最后 sink,其 他算子的并行度都为 2。整个程序包含了 7 个子任务,至少需要 2 个分区来并行执行。我们可以说,这段流处理程序的并行度就是 2。

3. 并行度的设置

在 Flink 中,可以用不同的方法来设置并行度,它们的有效范围和优先级别也是不同的。

( 1)代码中设置

我们在代码中,可以很简单地在算子后跟着调用 setParallelism()方法,来设置当前算子的 并行度:

stream.map(word -> Tuple2.of(word,1L)).setParallelism(2); 

这种方式设置的并行度,只针对当前算子有效。

另外,我们也可以直接调用执行环境的 setParallelism()方法,全局设定并行度:

env . setParallelism ( 2 ) ; 

这样代码中所有算子,默认的并行度就都为 2 了。我们一般不会在程序中设置全局并行度, 因为如果在程序中对全局并行度进行硬编码,会导致无法动态扩容。这里要注意的是,由于 keyBy 不是算子,所以无法对 keyBy 设置并行度。

(2)提交应用时设置

在使用 flink run 命令提交应用时,可以增加-p 参数来指定当前应用程序执行的并行度, 它的作用类似于执行环境的全局设置:

bin/flink run –p 2 –c com.atguigu.wc.StreamWordCount 
./FlinkTutorial-1.0-SNAPSHOT.jar 

如果我们直接在 Web UI 上提交作业,也可以在对应输入框中直接添加并行度。

(3)配置文件中设置

我们还可以直接在集群的配置文件 flink-conf.yaml 中直接更改默认并行度:

parallelism . default : 2 

这个设置对于整个集群上提交的所有作业有效,初始值为 1。无论在代码中设置、还是提 交时的-p 参数,都不是必须的;所以在没有指定并行度的时候,就会采用配置文件中的集群 默认并行度。在开发环境中,没有配置文件,默认并行度就是当前机器的 CPU 核心数。这也 就解释了为什么我们在第二章运行 WordCount 流处理程序时,会看到结果前有 1~4 的分区编 号——运行程序的电脑是 4 核 CPU,那么开发环境默认的并行度就是 4。

我们可以总结一下所有的并行度设置方法,它们的优先级如下:

( 1)对于一个算子,首先看在代码中是否单独指定了它的并行度,这个特定的设置优先级最高,会覆盖后面所有的设置。

(2)如果没有单独设置,那么采用当前代码中执行环境全局设置的并行度

(3)如果代码中完全没有设置,那么采用提交时-p 参数指定的并行度

(4)如果提交时也未指定-p 参数,那么采用集群配置文件中的默认并行度

这里需要说明的是,算子的并行度有时会受到自身具体实现的影响。比如之前我们用到的 读取 socket 文本流的算子 socketTextStream,它本身就是非并行的 Source 算子,所以无论怎么 设置,它在运行时的并行度都是 1,对应在数据流图上就只有一个并行子任务。这一点大家可 以自行在 Web UI 上查看验证。

那么实践中怎样设置并行度比较好呢?那就是在代码中只针对算子设置并行度,不设置全局并行度,这样方便我们提交作业时进行动态扩容

4.3.3 算子链(Operator Chain

很明显,这里的一个节点,会把转换处理的很多个任务都连接在一起,合并成了一个“大 任务”。

1 . 算子间的数据传输

一个数据流在算子之间传输数据的形式可以是一对一(one-to-one)的直 通 (forwarding)模式,也可以是打乱的重分区(redistributing)模式,具体是哪一种形式,取决于算子的种类。

( 1)一对一(One-to-one, forwarding)
这种模式下,数据流维护着分区以及元素的顺序。比如图中的 source 和 map 算子, source算子读取数据之后,可以直接发送给 map 算子做处理,它们之间不需要重新分区,也不需要 调整数据的顺序。这就意味着 map 算子的子任务,看到的元素个数和顺序跟 source 算子的子任务产生的完全一样,保证着“一对一”的关系。 map、 filter、 flatMap 等算子都是这种 one-to-one的对应关系。
这种关系类似于 Spark 中的窄依赖。
(2)重分区(Redistributing)
在这种模式下,数据流的分区会发生改变。比图中的 map 和后面的 keyBy/window 算子之间(这里的 keyBy 是数据传输算子,后面的 window、 apply 方法共同构成了 window 算子) ,以及 keyBy/window 算子和 Sink 算子之间,都是这样的关系。每一个算子的子任务,会根据数据传输的策略,把数据发送到不同的下游目标任务。例如,keyBy()是分组操作,本质上基于键(key)的哈希值(hashCode)进行了重分区;而当并行度 改变时,比如从并行度为 2 的 window 算子,要传递到并行度为 1 的 Sink 算子,这时的数据 传输方式是再平衡(rebalance),会把数据均匀地向下游子任务分发出去。这些传输方式都会 引起重分(redistribute)的过程,这一过程类似于 Spark 中的 shuffle。
总体说来,这种算子间的关系类似于 Spark 中的宽依赖。
2. 合并算子链
在 Flink 中,并行度相同的一对一(one to one)算子操作,可以直接链接在一起形成一个 “大”的任务(task),这样原来的算子就成为了真正任务里的一部分,如图 所示。每个 task会被一个线程执行。这样的技术被称为“算子链”(Operator Chain)。
Source 和 map 之间满足了算子链的要求,所以可以直接合并 在一起,形成了一个任务;因为并行度为 2,所以合并后的任务也有两个并行子任务。这样, 这个数据流图所表示的作业最终会有 5 个任务,由 5 个线程并行执行。

Flink 为什么要有算子链这样一个设计呢?这是因为将算子链接成task 是非常有效的优化:可以减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。 Flink 默认会按照算子链的原则进行链接合并,如果我们想要禁止合并或者自行定义,也 可以在代码中对算子做一些特定的设置:

/ / 禁用算子链 
. map (word -> Tuple2.of(word, 1L )).disableChaining () ; 
/ / 从当前算子开始新链 
. map (word -> Tuple2.of(word, 1L )).startNewChain ()

4.3.4 作业图(JobGraph)与执行图( ExecutionGraph

由 Flink 程序直接映射成的数据流图( dataflow graph),也被称为逻辑流图( logical StreamGraph),因为它们表示的是计算逻辑的高级视图。到具体执行环节时,我们还要考虑并 行子任务的分配、数据在任务间的传输,以及合并算子链的优化。为了说明最终应该怎样执行 一个流处理程序, Flink 需要将逻辑流图进行解析,转换为物理数据流图。
在这个转换过程中,有几个不同的阶段,会生成不同层级的图,其中最重要的就是作业图 (JobGraph)和执行图(ExecutionGraph)。
Flink 中任务调度执行的图,按照生成顺序可以分成 四层:
逻辑流图( StreamGraph)→ 作业图(JobGraph)→ 执行图(ExecutionGraph)→ 物理 图(Physical Graph)。
我们可以回忆一下之前处理 socket 文本流的 StreamWordCount 程序:
env . socketTextStream ( ) .flatMap (…).keyBy(0).sum(1).print(); 

如果提交时设置并行度为 2:

bin/ flink run –p 2 –c com.atguigu.wc.StreamWordCount 
. /FlinkTutorial-1.0-SNAPSHOT.jar 

那么根据之前的分析,除了 socketTextStream()是非并行的 Source 算子,它的并行度始终为1,其他算子的并行度都为 2。

1 . 逻辑流图(StreamGraph)

这是根据用户通过 DataStream API 编写的代码生成的最初的 DAG 图,用来表示程序的拓扑构。这一步一般在客户端完成。

我们可以看到,逻辑流图中的节点,完全对应着代码中的四步算子操作:

源算子 Source(socketTextStream())→扁平映射算子 Flat Map(flatMap()) →分组聚合算子

Keyed Aggregation(keyBy/sum()) →输出算子 Sink(print())。

2. 作业图(JobGraph)

StreamGraph 经过优化后生成的就是作业图(JobGraph),这是提交给 JobManager 的数据构,确定了当前作业中所有任务的划分。主要的优化为: 将多个符合条件的节点链接在一起 合并成一个任务节点,形成算子链,这样可以减少数据交换的消耗。 JobGraph 一般也是在客户端生成的,在作业提交时传递给 JobMaster。

在图中,分组聚合算子(Keyed Aggregation)和输出算子 Sink(print)并行度都为 2, 而且是一对一的关系,满足算子链的要求,所以会合并在一起,成为一个任务节点。

3. 执行图(ExecutionGraph)

JobMaster 收到 JobGraph 后,会根据它来生成执行图(ExecutionGraph)。 ExecutionGraph

是 JobGraph 的并行化版本,是调度层最核心的数据结构。从图中可以看到,与 JobGraph 最大的区别就是按照并行度对并行子任务进行了拆分, 并明确了任务间数据传输的方式。

4. 物理图(Physical Graph)

JobMaster 生成执行图后, 会将它分发给 TaskManager;各个 TaskManager 会根据执行图 部署任务,最终的物理执行过程也会形成一张“图”,一般就叫作物理图(Physical Graph)。 这只是具体执行层面的图,并不是一个具体的数据结构。

对应在上图中,物理图主要就是在执行图的基础上,进一步确定数据存放的位置和收发的具体方式。有了物理图, TaskManager 就可以对传递来的数据进行处理计算了。

所以我们可以看到,程序里定义了四个算子操作:源(Source) ->转换(flatMap) ->分组 聚合(keyBy/sum) ->输出(print);合并算子链进行优化之后,就只有三个任务节点了;再考 虑并行度后,一共有 5 个并行子任务,最终需要 5 个线程来执行。

 4.3.5 任务(Tasks)和任务槽(Task Slots)

1 . 任务槽(Task Slots)

之前已经提到过, Flink 中每一个 worker(也就是 TaskManager)都是一个 JVM 进程,它可以启动多个独立的线程,来并行执行多个子任务(subtask)。

所以如果想要执行 5 个任务,并不一定非要 5 个 TaskManager,我们可以让 TaskManager

多线程执行任务。如果可以同时运行 5 个线程,那么只要一个 TaskManager 就可以满足我们之 前程序的运行需求了。

很显然, TaskManager 的计算资源是有限的,并不是所有任务都可以放在一个 TaskManager

上并行执行。并行的任务越多,每个线程的资源就会越少。那一个 TaskManager 到底能并行处 理多少个任务呢?为了控制并发量,我们需要在 TaskManager 上对每个任务运行所占用的资源 做出明确的划分,这就是所谓的任务槽(task slots)。

每个任务槽(task slot)其实表示了 TaskManager 拥有计算资源的一个固定大小的子集。 这些资源就是用来独立执行一个子任务的。

 

 

假如一个 TaskManager 有三个 slot,那么它会将管理的内存平均分成三份,每个 slot 独自 占据一份。这样一来,我们在 slot 上执行一个子任务时,相当于划定了一块内存“专款专用”,就不需要跟来自其他作业的任务去竞争内存资源了。所以现在我们只要 2 个 TaskManager,就 可以并行处理分配好的 5 个任务了。

2. 任务槽数量的设置

我们可以通过集群的配置文件来设定 TaskManager 的 slot 数量:

taskmanager.numberOfTaskSlots:8 

通过调整 slot 的数量,我们就可以控制子任务之间的隔离级别。

具体来说,如果一个 TaskManager 只有一个 slot,那将意味着每个任务都会运行在独立的

JVM 中(当然,该 JVM 可能是通过一个特定的容器启动的);而一个 TaskManager 设置多个

slot 则意味着多个子任务可以共享同一个 JVM。它们的区别在于:前者任务之间完全独立运行, 隔离级别更高、彼此间的影响可以降到最小;而后者在同一个 JVM 进程中运行的任务,将共 享 TCP 连接和心跳消息,也可能共享数据集和数据结构,这就减少了每个任务的运行开销, 在降低隔离级别的同时提升了性能。

需要注意的是, slot 目前仅仅用来隔离内存,不会涉及 CPU 的隔离。在具体应用时,可 以将 slot 数量配置为机器的 CPU 核心数,尽量避免不同任务之间对 CPU 的竞争。这也是开发 环境默认并行度设为机器 CPU 数量的原因。

3. 任务对任务槽的共享

只要属于同一个作业,那么对于不同任务节点的并行子任务,就可以放到同一个 slot 上执行。 所以对于第一个任务节点 source→map,它的 6 个并行子任务必须分到不同的 slot 上(如果在 同一 slot 就没法数据并行了),而第二个任务节点 keyBy/window/apply 的并行子任务却可以和 第一个任务节点共享 slot。

于是最终结果就变成了:每个任务节点的并行子任务一字排开,占据不同的 slot;而不同 的任务节点的子任务可以共享 slot。一个 slot 中,可以将程序处理的所有任务都放在这里执行, 我们把它叫作保存了整个作业的运行管道(pipeline)

 

当我们将资源密集型和非密集型的任务同时放到 一个 slot 中,它们就可以自行分配对资源占用的比例,从而保证最重的活平均分配给所有的TaskManager。 slot 共享另一个好处就是允许我们保存完整的作业管道。这样一来,即使某个 TaskManager出现故障宕机,其他节点也可以完全不受影响,作业的任务可以继续执行。

另外,同一个任务节点的并行子任务是不能共享 slot 的,所以允许 slot 共享之后,运行作 业所需的 slot 数量正好就是作业中所有算子并行度的最大值。这样一来,我们考虑当前集群需 要配置多少 slot 资源时,就不需要再去详细计算一个作业总共包含多少个并行子任务了,只看 最大的并行度就够了。

当然, Flink 默认是允许 slot 共享的,如果希望某个算子对应的任务完全独占一个 slot, 或者只有某一部分算子共享 slot,我们也可以通过设置“slot 共享组”( SlotSharingGroup)手动 指定:

.map(word -> Tuple2 . of(word,1L)).slotSharingGroup(“1”); 

4. 任务槽和并行度的关系

Slot 和并行度确实都跟程序的并行执行有关,但两者是完全不同的概念。简单来说, task slot 是 静 态 的 概 念 , 是 指 TaskManager 具 有 的 并 发 执 行 能 力 , 可 以 通 过 参 数taskmanager.numberOfTaskSlots 进行配置;而并行度( parallelism)是动态概念,也就是TaskManager 运行程序时实际使用的并发能力,可以通过参数 parallelism.default 进行配置。

并行度如果小于等于集群中可用 slot 的总数,程序是可以正常执行的,因为 slot 不一 定要全部占用,有十分力气可以只用八分;而如果并行度大于可用 slot 总数,导致超出了并行 能力上限,那么心有余力不足,程序就只好等待资源管理器分配更多的资源了。 

 

 

如上图所示,我们应该怎么提高效率呢?

首先,肯定不能让有Solt空闲,所以我们要把并行度设置为9,考虑到输出的时候可能是写入文件,所以我们应该将sink放到一个Solt,将Sink算子的并行度设为1,其它算子的并行度依然为9,这样就可以避免生成多个文件。 


 

 

 

相关文章:

  • Python学习:获取对象信息
  • 人工神经网络理论及应用,人工智能神经网络论文
  • 【数据结构与算法】之深入解析“考试的最大困扰度”的求解思路与算法示例
  • SpirngMVC获取请求参数
  • [Spring Cloud 项目] Spring cloud 实现房源查询功能
  • golang设计模式——结构模式
  • CF803G Periodic RMQ Problem【动态开点线段树+ST表】
  • 【业务知识】发票系统设计知识学习二
  • Basic Facilities of a Virtio Device (二)
  • redis五种数据类型对应的底层数据结构
  • Linux 必会基础语句 软硬连接区别 Linux文件类型
  • 【PAT甲级】1141 PAT Ranking of Institutions
  • JAVA基础(三十二)——反射之创建对象
  • java优秀毕业生推荐系统ssm
  • Group DETR
  • 《Java编程思想》读书笔记-对象导论
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • canvas 五子棋游戏
  • Elasticsearch 参考指南(升级前重新索引)
  • Fabric架构演变之路
  • flask接收请求并推入栈
  • Java 最常见的 200+ 面试题:面试必备
  • JavaScript实现分页效果
  • javascript数组去重/查找/插入/删除
  • mac修复ab及siege安装
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • Python实现BT种子转化为磁力链接【实战】
  • Redis 中的布隆过滤器
  • Spring Boot MyBatis配置多种数据库
  • SQLServer之索引简介
  • Vue2.x学习三:事件处理生命周期钩子
  • yii2中session跨域名的问题
  • 当SetTimeout遇到了字符串
  • 如何胜任知名企业的商业数据分析师?
  • 如何用vue打造一个移动端音乐播放器
  • 使用权重正则化较少模型过拟合
  • 数据库巡检项
  • ​ArcGIS Pro 如何批量删除字段
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #NOIP 2014# day.1 T2 联合权值
  • (0)Nginx 功能特性
  • (C语言)共用体union的用法举例
  • (HAL库版)freeRTOS移植STMF103
  • (定时器/计数器)中断系统(详解与使用)
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • ***监测系统的构建(chkrootkit )
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .net专家(高海东的专栏)
  • @javax.ws.rs Webservice注解
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [Angular] 笔记 20:NgContent