Flink Runtime 杂记

Apache Flink的Runtime核心机制剖析

Flink是可以运行在多种环境下的(yarn,k8s,云环境,),它可以单进程和多线程方式启动,便于我们调式代码

Flink Runtime 整体架构
针对不同的执行环境,Flink提供了统一的分布式作业引擎,就是Flink Runtime这层,
Flink在Runtime上提供了DataSetAPI(Gelly,Table,FlinkML) 和 DataStreamAPI(CEP,Table)
整体来说是采用master-slave架构
master 负责集群资源和作业
master有三个组件组成Dispatcher,ResourceManager,jobManager
Dispatcher负责接收用户提供的作业,并且为作业启动一个新的jobManager
ResourceManager资源的管理,整个Flink集群只有一个ResourManager
jobmanager负责管理作业的执行,每个作业都有自己的jobmanager。一个flink可以同时的运行多个作业。
这个三个组件都在AppMater进程中

slave 负责具体的资源和执行任务

当用户提交一个作业的时候,提交脚本会启动一个clinet进程,负责对作业进行编译和提交,对用户的代码编译成jobgraph,并对它进行优化和检查,判断哪些operator是可以chain到同一个Task中,然后将jobgraph提交到集群。

提交有两种
1:一种是类似standalone的session模式,此时的AM已经启动,我们只需要提交给Dispatcher就行了。
2:**per-job,**AM是不会提前启动,此时client向资源管理系统k8s,yarn等申请资源,再把任务提交到Dispatcher,Dispatcher会启动jobmanager,jobmanager向ResourceManager申请运行资源来启动作业中的具体任务。
TaskExecutor
根据模式的不同,如果是session模式的话,ResourceManager可能已经有了TaskExecutor的注册,可以直接分配 TaskExecution的资源。
如果是per-job的话,ResourceManager需要向外部的资源系统(yarn,k8s等)申请资源来启动TaskExecutor,然后等待TaskManager注册好相应的资源后,在分配空闲资源来运行任务。
目前TaskExecutor的志资源是通过Solt来描述的,一个Solt一般可以执行一个具体的任务,也可以执行相关联的Task。
RM选择好空闲的solt以后,就通知TaskManagr把slot给Jobmanager xx, 然后TaskExecutor向JobManager进行注册,JobManager收到Slot注册后,就可以提交Task了。
TaskExecutor收到jobmanager的任务后,启动新的线程来运行Task,Task启动后开始执行预先指定的运算,并可以通过数据的shuffle模块进行数据交换。

以上就是 Flink Runtime 层执行作业的基本流程
Flink支持两种不同的模式 Per-job 和 Session 模式,在Per-job模式下Flink集群是只运行单个作业,每个作业独享Dispatcher和ResourceManager,在此模式下AM和TE是按需申请的,
Per-job 模式更适合运行执行时间较长的大作业,这些作业对稳定性要求较高,并且对申请资源的时间不敏感。与之对应,在 Session 模式下,Flink 预先启动 AppMaster 以及一组 TaskExecutor,然后在整个集群的生命周期中会执行多个作业。可以看出,Session 模式更适合规模小,执行时间短的作业。

资源管理和任务调度
作业调度可以是看做是任务和资源匹配的过程,调度的主要目的是为task找到合适的slot,每个slot都必须要使用向量来表示slot提供各种资源的量,Task也需要使用向量来说明它所需要各种资源的量。

作业的调度的基础是对资源的管理
Flink的资源是由TaskExecutor的slot来表示的,ResourceManager中有一个子组件SlotManager对集群中TaskExecutor的slot进行管理,
SlotManager中包含Flink集群中所有的Slot的位置信息和Slot是否空闲等消息。SlotManager中有Slot的信息和状态。
根据不同的模式,JM可能会向RM申请资源来启动TE,TE会向活跃的RM进行注册,在注册的时候,会把TE中Slot的状态和信息注册到RM的SM中。
RM向JM返回空闲的Slot的信息,RM选择好了Slot后向TM发送RPC要求,让TM把选好的Slot提供给特定JM。
如果TM没有为JM提供过slot的话或还没有为JM执行过Task的话,先跟JM建立连接,然后先向JM发送提供Slot的RPC请求。
JM的Task请求缓存到SlotPool中,当有Slot提供的时候,SlotPool会从缓存中选择相应的请求过程进行结束。
当Task结束之后,无论是异常还是正常都要通知JM相应的状态,然后在TM端把Slot标记为占用但未执行任务的状态。
JM会将Slot缓存到SlotPool中,不会立即释放slot,还回给RM,这种方式的好处是当Task失败的时候,不用重新向RM申请TE的Slot了。
通过延时释放的方法,Failover的Task可以迅速的调回原来的TM,可以加速Task的Failover。
当在一定的时间(指定的时间)内没有引用Slot,SlotPool会启动释放slot的过程。
SlotPool向TM发送释放Slot,然后TE通知RM该Slot已经被释放了。
RM和TE还存在有定时的心跳信息用来同步RM中的Slot信息。
默认情况下,每个slot只执行一个Task,但是资源的利用率较低,Flink提供了一个Share slot 机制
每个 Slot 中最多可以部署同一个 A、B 或 C 的 Task,但是可以同时部署 A、B 和 C 的各一个 Task。当单个 Task 占用
资源较少时,Share Slot 可以提高资源利用率。 此外,Share Slot 也提供了一种简单的保持负载均衡的方式

JM负责维护Task的执行状态,client向JM提交jobgraph,jobGraph是作业的执行逻辑,JM会根据jobGraph按并发进行展开,生成ExecutionGraph,ExecutoionGraph对于每个Task与中间结果等都创建对象,从而可以维护这些实体的信息与状态。
ExecutionGraph 是 JobGraph 按并发展开所形成的,它是JobMaster中的核心数据结构.

Flink job中是有多个Task的,Flink是按什么顺序来调度Task的呢?
Flink提供了两种的调度逻辑:
Eager 调度
它会在启动作业时,申请资源将Task都调动起来,适合用于流计算的调度。
Lazy From Source 调度
从 Source 开始,按拓扑顺序来进行调度,先调度没有上游任务的 Source 任务,当这些任,务执行完成时,它会将输出数据缓存到内存或者写入到磁盘中。然后,对于后续的任务,当它的前驱任务全部执行完成后,Flink 就会将这些任务调度起来。这些任务会从读取上游缓存的输出数据进行自己的计算。这一过程继续进行直到所有的任务完成计算。
Flink 中两种基本的调度策略。其中 Eager 调度适用于流作业,而 Lazy From Source 适用于批作业
错误恢复
Task 执行出现错误或 Flink 集群的 Master 出现错误。由于错误不可避免,为了提高可用性,Flink 需要提供自动错误恢复机制来进行重试

Flink 提供了多种不同的错误恢复策略。
第一种策略是 Restart-all,即直接重启所有的 Task。对于 Flink 的流任务,由于 Flink 提供了 Checkpoint 机制,因此当任务重启后可以直接从上次的Checkpoint 开始继续执行。因此这种方式更适合于流作业。

第二类错误恢复策略是
Restart-individual,它只适用于 Task 之间没有数据传输的情况。这种情况下,我们可以直接重启出错的任务

由于 Flink 的批作业没有 Checkpoint 机制,因此对于需要数据传输的作业,直接重启所有 Task 会导致作业从头计算,从而导致一定的性能问题。为了增强对Batch 作业,Flink 在 1.9 中引入了一种新的 Region-Based 的 Failover 策略

为了增强对 Batch 作业,Flink 在 1.9 中引入了一种新的 Region-Based 的 Failover 策略。在
一个 Flink 的 Batch 作业中 Task 之间存在两种数据传输方式,
一种是 Pipeline 类型的方式,这种方式上下游 Task 之间直接通过网络传输数据,因此需要上下游同时运行;
另外一种是 Blocking 类型的试,如上节所述,这种方式下,上游的 Task 会首先将数据进行缓存,因此上下游的 Task 可以单独执行。基于这两种类型的传输,Flink 将 ExecutionGraph 中使用 Pipeline 方式传输数据的 Task 的子图叫做
Region,从而将整个 ExecutionGraph 划分为多个子图。

可以看出,Region 内的 Task 必须同时重启,而不同 Region 的 Task 由于在 Region 边界存在 Blocking 的
边,因此,可以单独重启下游 Region 中的 Task。

基于这一思路 ,
如果某个 Region 中的某个 Task 执行出现错误,可以分两种
情况进行考虑。如果是由于 Task 本身的问题发生错误,那么可以只
重启该 Task 所属的 Region 中的 Task,
这些 Task 重启之后,可以直接拉取上游Region 缓存的输出结果继续进行计算。
另一方面,如果错误是由于读取上游结果出现问题,如网络连接中断、缓存
上游输出数据的 TaskExecutor 异常退出等,那么还需要重启上游 Region 来重新
产生相应的数据。在这种情况下,如果上游 Region 输出的数据分发方式不是确定性
的(如 KeyBy、Broadcast 是确定性的分发方式,而 Rebalance、Random 则不
是,因为每次执行会产生不同的分发结果),为保证结果正确性,还需要同时重启上
游 Region 所有的下游 Region
除了 Task 本身执行的异常外,另一类异常是 Flink 集群的 Master 进行发生异
常。目前 Flink 支持启动多个 Master 作为备份,这些 Master 可以通过 ZK 来进行
选主,从而保证某一时刻只有一个 Master 在运行。
当前活路的 Master 发生异常
时 , 某个备份的 Master 可以接管协调的工作。为了保证 Master 可以准确维护作业
的状态,Flink 目前采用了一种最简单的实现方式,
即直接重启整个作业。实际上,
由于作业本身可能仍在正常运行,因此这种方式存在一定的改进空间。

5. 未来展望
Flink 目前仍然在 Runtime 部分进行不断的迭代和更新。目前来看,Flink 未来
可能会在以下几个方式继续进行优化和扩展:
● 更完善的资源管理:从 1.9 开始 Flink 开始了对细粒度资源匹配的支持。基于
细粒度的资源匹配,用户可以为 TaskExecutor 和 Task 设置实际提供和使用
的 CPU、内存等资源的数量,Flink 可以按照资源的使用情况进行调度。这一
机制允许用户更大范围的控制作业的调度,从而为进一步提高资源利用率提供
了基础。
● 统一的 Stream 与 Batch:Flink 目前为流和批分别提供了 DataStream 和
DataSet 两套接口,在一些场景下会导致重复实现逻辑的问题。未来 Flink 会
将流和批的接口都统一到 DataStream 之上。
● 更灵活的调度策略:Flink 从 1.9 开始引入调度插件的支持,从而允许用户来
扩展实现自己的调度逻辑。未来 Flink 也会提供更高性能的调度策略的实现。
● Master Failover 的优化:如上节所述,目前 Flink 在 Master Failover 时
需要重启整个作业,而实际上重启作业并不是必须的逻辑。Flink 未来会对
Master failover 进行进一步的优化来避免不必要的作业重启。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值