Flink在资源管理上可以分为两层:集群资源和自身资源。集群资源支持主流的资源管理系统,如yarn、mesos、k8s等,也支持独立启动的standalone集群。自身资源涉及到每个子task的资源使用,由Flink自身维护。
1 集群架构剖析
Flink的运行主要由 客户端、一个JobManager(后文简称JM)和 一个以上的TaskManager(简称TM或Worker)组成。
客户端
客户端主要用于提交任务到集群,在Session或Per Job模式中,客户端程序还要负责解析用户代码,生成JobGraph;在Application模式中,直接提交用户jar和执行参数即可。客户端一般支持两种模式:detached模式,客户端提交后自动退出。attached模式,客户端提交后阻塞等待任务执行完毕再退出。
JobManager
JM负责决定应用何时调度task,在task执行结束或失败时如何处理,协调检查点、故障恢复。该进程主要由下面几个部分组成:
1 ResourceManager,负责资源的申请和释放、管理slot(Flink集群中最细粒度的资源管理单元)。Flink实现了多种RM的实现方案以适配多种资源管理框架,如yarn、mesos、k8s或standalone。在standalone模式下,RM只能分配slot,而不能启动新的TM。注意:这里所说的RM跟Yarn的RM不是一个东西,这里的RM是JM中的一个独立的服务。
2 Dispatcher,提供Flink提交任务的rest接口,为每个提交的任务启动新的JobMaster,为所有的任务提供web ui,查询任务执行状态。
3 JobMaster,负责管理执行单个JobGraph,多个任务可以同时在一个集群中启动,每个都有自己的JobMaster。注意这里的JobMaster和JobManager的区别。
TaskManager
TM也叫做worker,用于执行数据流图中的任务,缓存并交换数据。集群至少有一个TM,TM中最小的资源管理单元是Slot,每个Slot可以执行一个Task,因此TM中slot的数量就代表同时可以执行任务的数量。
2 Slot与资源管理
每个TM是一个独立的JVM进程,内部基于独立的线程执行一个或多个任务。TM为了控制每个任务的执行资源,使用task slot来进行管理。每个task slot代表TM中的一部分固定的资源,比如一个TM有3个slot,每个slot将会得到TM的1/3内存资源。不同任务之间不会进行资源的抢占,注意GPU目前没有进行隔离,目前slot只能划分内存资源。
比如下面的数据流图,在扩展成并行流图后,同一的task可能分拆成多个任务并行在集群中执行。操作链可以把多个不同的任务进行合并,从而支持在一个线程中先后执行多个任务,无需频繁释放申请线程。同时操作链还可以统一缓存数据,增加数据处理吞吐量,降低处理延迟。
在Flink中,想要不同子任务合并需要满足几个条件:下游节点的入边是1(保证不存在数据的shuffle);子任务的上下游不为空;连接策略总是ALWAYS;分区类型为ForwardPartitioner;并行度一致;当前Flink开启Chain特性。
在集群中的执行图可能如下:
Flink也支持slot的共享,即把不同任务根据任务的依赖关系分配到同一个Slot中。这样带来几个好处:方便统计当前任务所需的最大资源配置(某个子任务的最大并行度);避免Slot的过多申请与释放,提升Slot的使用效率。
通过Slot共享,就有可能某个Slot中包含完整的任务执行链路。
3 应用执行
一个Flink应用就是用户编写的main函数,其中可能包含一个或多个Flink的任务。这些任务可以在本地执行,也可以在远程集群启动,集群既可以长期运行,也支持独立启动。下面是目前支持的任务提交方案:
Session集群
生命周期:集群事先创建并长期运行,客户端提交任务时与该集群连接。即使所有任务都执行完毕,集群仍会保持运行,除非手动停止。因此集群的生命周期与任务无关。
资源隔离:TM的slot由RM申请,当上面的任务执行完毕会自动进行释放。由于多个任务会共享相同的集群,因此任务间会存在竞争,比如网络带宽等。如果某个TM挂掉,上面的所有任务都会失败。
其他方面:拥有提前创建的集群,可以避免每次使用的时候过多考虑集群问题。比较适合那些执行时间很短,对启动时间有比较高的要求的场景,比如交互式查询分析。
Per Job集群
生命周期:为每个提交的任务单独创建一个集群,客户端在提交任务时,直接与ClusterManager沟通申请创建JM并在内部运行提交的任务。TM则根据任务运行需要的资源延迟申请。一旦任务执行完毕,集群将会被回收。
资源隔离:任务如果出现致命问题,仅会影响自己的任务。
其他方面:由于RM需要申请和等待资源,因此启动时间会稍长,适合单个比较大、长时间运行、需要保证长期的稳定性、不在乎启动时间的任务。
Application集群
生命周期:与Per Job类似,只是main()方法运行在集群中。任务的提交程序很简单,不需要启动或连接集群,而是直接把应用程序打包到资源管理系统中并启动对应的EntryPoint,在EntryPoint中调用用户程序的main()方法,解析生成JobGraph,然后启动运行。集群的生命周期与应用相同。
资源隔离:RM和Dispatcher是应用级别。