Flink1——运行时架构

Flink1——运行时架构

Flink系统架构

  1. 整体构成

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

  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 执行任务,互相之间可以交换数据。

重要概念

  1. 数据流图

在 Flink 代码中,我们定义的每一个处理转换操作都叫作“算子”(Operator),程序可以看作是一串算子构成的管道,数据则像水流一样有序地流过。

所有的 Flink 程序都可以归纳为由三部分构成:Source、Transformation 和 Sink。
⚫ Source 表示“源算子”,负责读取数据源。
⚫ Transformation 表示“转换算子”,利用各种算子进行处理加工。
⚫ Sink 表示“下沉算子”,负责数据的输出。

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

  1. 并行度

(1)什么是并行计算

并行计算不是指任务并行,而是数据并行,也就是说多条数据同时到来,可以同时读入,同时在不同结点执行flatMap操作。

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

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

一个特定算子的子任务(subtask)的个数被称之为其并行度(parallelism)

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

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

//这种方式设置,只针对当前算子有效
stream.map(word -> Tuple2.of(word, 1L)).setParallelism(2);

//在执行环境中设置,这样所有算子的并行度都为2
env.setParallelism(2);

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

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

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

c.配置文件中设置

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

parallelism.default: 2

这个设置对于整个集群上提交的所有作业有效,初始值为 1。在其它没有指定并行度的时候,就会采用配置文件中的集群默认并行度。在开发环境中,没有配置文件,默认并行度就是当前机器的CPU核心数。

d.并行度优先级

  • 对于一个算子,首先看在代码中是否单独指定了它的并行度,这个特定的设置优先级 最高,会覆盖后面所有的设置。
  • 如果没有单独设置,那么采用当前代码中执行环境全局设置的并行度
  • 如果代码中完全没有设置,那么采用提交时**-p 参数**指定的并行度。
  • 如果提交时也未指定-p 参数,那么采用集群配置文件中的默认并行度

PS:算子的并行度有时会受到自身具体实现的影响,比如读取 socket 文本流的算子 socketTextStream,它本身就是非并行的 Source 算子,所以无论怎么设置,它在运行时的并行度都是 1。

  1. 算子链

(1)算子间的数据传输

  • 一对一(one-to-one)
    如source 和 map 算子,source
    算子读取数据之后,可以直接发送给 map 算子做处理,它们之间不需要重新分区,也不需要调整数据的顺序。

  • 重分区(Redistributing)

在这种模式下,数据流的分区会发生改变。比图中的 map 和后面的 keyBy/window 算子之间,以及 keyBy/window 算子和 Sink 算子之间,都是这样的关系。

(2)合并算子链
在 Flink 中,并行度相同的一对一(one to one)算子操作,可以直接链接在一起形成一个“大”的任务(task),这样原来的算子就成为了真正任务里的一部分。

在这里插入图片描述

Source 和 map 之间满足了算子链的要求,所以可以直接合并在一起,形成了一个任务;因为并行度为 2,所以合并后的任务也有两个并行子任务。这样,这个数据流图所表示的作业最终会有 5 个任务,由 5 个线程并行执行。

将算子链接成 task 是非常有效的优化:可以减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量

  1. 作业图和执行图

Flink 中任务调度执行的图,按照生成顺序可以分成四层:
逻辑流图(StreamGraph)→ 作业图(JobGraph)→ 执行图(ExecutionGraph)→ 物理图(Physical Graph)。

逻辑流图表示了程序的拓扑结构,作业图 将多个符合条件的节点链接在一起合并成一个任务节点形成算子链,执行图按照并行度对并行子任务进行了拆分,并明确了任务间数据传输的方式,物理图主要就是在执行图的基础上,进一步确定数据存放的位置和收发的具体方式。

  1. 任务和任务槽(Task Slots)

(1)任务槽

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

任务槽(task slot)其实表示了 TaskManager 拥有计算资源的一个固定大小的子集,这些资源就是用来独立执行一个子任务的。在这里插入图片描述

如一个 TaskManager 有三个 slot,那么它会将管理的内存平均分成三份,每个 slot 独自占据一份。所以现在只要 2 个 TaskManager,就可以并行处理分配好的 5 个任务。

(2)任务槽数量的设置

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

taskmanager.numberOfTaskSlots: 8

(3)任务对任务槽的共享

保持 sink 任务并行度为 1 不变,而作业提交时设置全局并行度为 6,那么前两个任务节点就会各自有 6 个并行子任务,整个流处理程序则有 13 个子任务。这样对于 2 个 TaskManager、每个有 3 个 slot 的集群配置来说,也还是可以正常运行的。

在这里插入图片描述

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

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

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

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

这样,只有属于同一个 slot 共享组的子任务,才会开启 slot 共享;不同组之间的任务是完全隔离的,必须分配到不同的 slot 上。在这种场景下,总共需要的 slot 数量,就是各个 slot共享组最大并行度的总和。

(4)任务槽和并行度的关系

Slot 和并行度确实都跟程序的并行执行有关,但两者是完全不同的概念。简单来说,task slot 是 静 态 的 概 念 , 是 指 TaskManager 具 有 的 并 发 执 行 能 力 , 可 以 通 过 参 数taskmanager.numberOfTaskSlots 进行配置;而并行度(parallelism)是动态概念,也就是TaskManager 运行程序时实际使用的并发能力,可以通过参数 parallelism.default 进行配置。换
句话说,并行度如果小于等于集群中可用 slot 的总数,程序是可以正常执行的,因为 slot 不一定要全部占用,有十分力气可以只用八分;而如果并行度大于可用 slot 总数,导致超出了并行能力上限,那么心有余力不足,程序就只好等待资源管理器分配更多的资源了。

整个流处理程序的并行度,就应该是所有算子并行度中最大的那个,这代表了运行程序需要的 slot 数量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值