摘要
本文主要对Flink程序运行时的架构做一个简单的总结,主要涉及到Flink任务的提交流程,运行流程,以及提交和运行时用到一些组件之间的关系,包括:JobManager,TaskManager,Client;以及运行时task,sub task,operator,operator chain,slot之间的关系。
参考:https://www.cnblogs.com/wyh-study/p/12623118.html
提交流程
Flink任务提交时主要涉及到Client,JobManager(appmaster),TaskManager几个概念。
Flink on yarn提交任务流程图
- Client(客户端)提交任务jar包和配置信息到HDFS;
- Client(客户端)提交Job到ResourceManager,ResourceManager开辟资源用于启动JobManager;
- ResourceManager分配资源启动JobManager;
- JobManager向ResourceManager申请资源用于启动TaskManager。
Client
即客户端,提交Flink任务到yarn集群的机器,任务提交成功后Client可以结束进程,也可以不结束进程等待返回结果,此时任务已经托管给yarn集群;
JobManager
负责整个Flink任务调度,负责调度Job并协调Task做checkpoint,从Client接收jar包和配置之后,会生成优化后的执行计划,并以Task为单元调度到各个TaskManager上去。
- 控制一个应用程序执行的主进程,也就是说每个应用程序都会被不同JobManager控制执行;
- JobManager会先接收到要执行的应用程序,这个应用程序会包括:作业图(JobGraph),逻辑数据流图(logical dataflow graph)和打包了所有的类,库,配置文件和其它资源的资源的jar包;
- JobManager会把JobGraph转换成一个物理层面的数据流图,这个图叫做“执行图”(ExecutionGraph),包含了所有可以并发执行的任务;
- JobManager会向资源管理器(ResourceManger)请求执行任务必要的资源,也就是任务管理器(TaskManager)上的插槽(slot)。一旦他获取了足够的资源,就会将执行图分发到真正运行他们的TaskManager上,而在运行过程中,JobManager会负责所有需要中央协调的操作,比如检查点(checkpoints)的协调。
TaskManager
Flink任务真正执行单元,负责执行JobManager分配的task,并定时向JobManager同步心跳信息。TaskManager会启动多个slot来执行Job分配的task,同一个Flink任务的同一个TaskManager上slot会平均分配申请的内存,共享TCP通信协议,心跳和checkpoint,共享一些数据资源(广播变量等),注意CPU未做隔离。在执行过程中同一任务的多个TaskManager可以进行数据传输交换,一个TaskManager代表一个进程,他下面的task是线程。
- Flink中的工作进程,通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含一定数量的插槽(slots)。插槽的数量限制了TaskManager能够并行执行的任务数量。
- 启动之后,TaskManager会向资源管理器注册它的插槽,收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给JobManager调用。JobManager就可以向插槽分配任务(Tasks)来执行了。
- 在执行过程中,一个TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。
Flink任务调度原理图
Flink 任务调度过程基本都是Client,JobManager,TaskManager这几个角色。
资源管理分配
资源管理分配主要包括task,subtask,slot,operator,operator chain,以及他们之间关系;
operator:操作算子,常见map,filter,flatMap.....都算一个operator;
task:Flink任务中task的划分是根据代码中operator来划分的,由一个或多个operator组成,如果一个operator操作会使上一个operator产生的数据流从新分区(例如:keyby(分组),rebalance(随机重新分区),broadcast(广播),并行度改变),,则此时会切换成一个新的task。
上图中一共有三个task,task1和task2的虽然并行度相同,但是中间调用了rebalance()算子数据从新分区,所以切分成了两个task,task2和task3由于并行度不同,也会导致数据从新分区。
subtask:subtask算是task的子集,即一个task中会包含多个sub task,是Flink任务并行执行的表现,上图中sub task个数为21+21+1=43,21,21,1为每个task的执行并行度,
operator chain:是将多个operator 操作组合在一起,可以包含一个或多个operator chain,例如task1中的operator chain 只有source一个operator,而task2中包含了map,map,filter三个operator。
operator chain的组成条件:
- 没有禁用operator chain;
- 上下游算子并行度一致;
- 下游算子的入度为1(也就是下游节点没有来自其他节点的输入);
- 上下游算子在同一个slot group;
- 下游节点的chain策略为always(可以与上下游连接,map,filter,flatMap等默认都是ALWAYS);
- 上游节点chain策略为always或head(只能与下游链接,不能与上游链接,source默认是head);
- 上下游算子之间没有数据shuffle(数据分区方式是forward)。
operator chain的优点:
- 减少数据序列化和反序列消耗(不需要数据之间的交换传输,属于一个pipline的操作);
- 减少线程切换;
- 减少数据据在缓冲区的交换;
- 减少延迟并且提高吞吐能力。
operator chain改变方式:
operator chain可以通过编程API进行指定。
- 可以通过DataStream的operator后面调用startNewChain()来指示从该operator开始一个新的chain(与前面截断,不会被chain到前面);
- 调用disableChaining()来指示该operator不参与chaining(不会与前后的operator chain一起);
- 通过SreamExecutionEnvironment.disableOperatorChaining()来全局禁用chaining;
- 设置Slot group,例如someStream.filter(...).slotSharingGroup("name");
- 调用并行度,setParallelism()。
slot 共享
slot 共享是指,一个TaskManager 上的一个slot可以运行多个sub tasks(oprerator chain)。
默认情况下,Flink允许subtasks共享slot,条件是它们来自同一个Job的不同task的subtask。共享slot有两个优点,1.提高资源的利用率,2.Flink 集群需要的任务槽与作业的最高并行度正好相同(前提:保持默认的slotSharingGroup)。
上图可以看一次一个slot中会运行过个subtask。
Slots和parallelism的关系
首先在启动Flink的任务书Slots个数和parallelism的个数都是可以单独设置的。
Slot可以通过:
- -yn启动多个TaskManager,
- -ys:一个TaskManager上启动多个slot来设置;
slot的总个数就等于:yn * ys
parallelism可以通过:
- 在配置文件$FLINK_HOME/conf/flink-conf.yaml 中配置 parallelism.default;
- 启动脚本 -p ;
env.setParallelism(10);
每个算子后调用 setParallelism(10) 方法来指定
优先级:4 > 3 > 2 > 1
个人在设置的并行度时比较倾向于第二种,之间在启动脚本中设置,这样如果在任务需要扩大并行度的时候不需要去修改代码,只需要修改一下脚本,重新启动即可,比较方便。
需要注意的是如果一个Flink的最大并行度大于申请Slot个数(在不设置SlotSharingGroup的情况下),那么任务无法启动;但是如果Slot的个数大于最大的并行度,那么部分的Slot处于空闲状态,也会造成资源浪费。
如何计算一个应用需要多少个slot?
1.不设置SlotSharingGroup(应用的最大并行度);
2.设置SlotSharingGroup(所有的SlotSharingGroup中最大并行度之和),因为设置了SlotSharingGroup,各个slot group之间是无法共享slot的。