【Flink原理和应用】:Akka和Actors

原文:https://cwiki.apache.org/confluence/display/FLINK/Akka+and+Actors#space-menu-link-content

前言

本文重点讨论Flink通过Akka实现分布式通信,Akka在Flink 0.9版本中已经开始采用。使用Akka,所有远程过程调用过程都实现为异步消息。这主要影响组件JobManager、TaskManager和JobClient。将来,可能还会有更多的组件被转换为actor模型,允许它们发送和处理异步消息。

1. Akka和Actor模型

Akka是一个开发并发、容错和可伸缩应用程序的框架。它是actor模型的实现,因此类似于Erlang的并发模型。在actor模型的上下文中,所有代理实体都被认为是独立的actor。actor通过向彼此发送异步消息与其他actor通信。actor模型的强度来自于这种异步。还可以显式地等待允许执行同步操作的响应。但是,我们一般强烈反对同步消息,因为它们限制了系统的可伸缩性。每个actor都有一个邮箱,其中存储了接收到的消息。此外,每个actor都保持其自身的隔离状态。下面给出几个参与者的示例网络。
在这里插入图片描述

actor只有一个处理线程,该线程轮询actor的邮箱并连续处理接收到的消息。作为已处理消息的结果,actor可以更改其内部状态、发送新消息或生成新actor。如果actor的内部状态是从其处理线程中独占操纵的,那么就不需要确保actor的状态线程安全。尽管单个actor本质上是顺序的,但是由多个actor组成的系统具有高度并发性和可伸缩性,因为处理线程在所有actor之间共享。这种共享也是为什么永远不应该从actor线程内调用阻塞调用的原因。这样的调用将阻止线程被其他actor用于处理他们自己的消息。

2. Actor系统

Actor系统是所有actor存活的容器,它提供诸如调度、配置和日志记录之类的共享服务。Actor系统还包含从所有actor线程收集到的线程池。

多个Actor系统可以在单个机器上共存。如果Actor系统是用RemoteActorRefProvider启动的,那么可以从可能驻留在远程计算机上的另一个Actor系统访问它。Actor系统自动识别actor消息是发给生活在同一Actor系统或远程Actor系统中的actor的。在本地通信的情况下,使用共享存储器有效地传输消息。在远程通信的情况下,通过网络堆栈发送消息。

所有actor都按层次结构组织。每个新创建的actor将其创建的actor作为父节点分配。层次结构用于监督。每位家长负责监督其子女。如果其中一个子节点出现错误,则通知他。如果actor能够解决问题,那么他可以恢复或重新启动他的孩子。如果问题超出了它的处理范围,它可以将错误升级到自己的父母。升级错误仅仅意味着当前层次之上的层次结构层现在负责解决问题。有关Akka的监督和监测的细节可以在这里找到

系统创建的第一个actor由系统提供的监护actor/用户监控。这里将深入解释角色层次结构。有关Actor系统的更多信息,请参阅这里

3. Actors在Flink中的应用

actor本身是状态和行为的容器。它的actor线程顺序地处理传入消息。它减轻了用户锁定和线程管理的易出错任务,因为一次只有一个线程对一个actor是活动的。但是,必须确保仅从该actor线程访问actor的内部状态。actor的行为由接收函数定义,该接收函数为每个消息包含接收该消息时执行的一些逻辑。

Flink系统由三个必须通信的分布式组件组成:JobClient、JobManager和TaskManager。JobClient从用户那里获取Flink作业并将其提交给JobManager。然后,JobManager负责编排作业执行。首先,它分配所需的资源量,这主要包括任务管理器上的执行槽。

在资源分配之后,JobManager将作业的各个任务部署到相应的TaskManager。在接收到任务时,TaskManager生成执行任务的线程。诸如开始计算或完成计算之类的状态更改被发送回JobManager。基于这些状态更新,JobManager将指导作业执行直到完成。一旦作业完成,它的结果将被发送回JobClient,JobClient告诉用户有关它的信息。作业执行过程如下图所示。

在这里插入图片描述

4. JobManager & TaskManager

JobManager是负责执行Flink作业的中央控制单元。因此,它管理资源分配、任务调度和状态报告。

在执行任何Flink作业之前,必须启动一个JobManager和一个或多个TaskManager。然后,TaskManager通过向JobManager发送RegisterTaskManager消息在JobManager注册。JobManager通过AcknowledgeRegisting消息确认注册成功。如果任务管理器已经在JobManager注册,因为发送了多个RegisterTaskManager消息,JobManager将返回AlreadyRegistered消息。如果注册被拒绝,则JobManager将回复RefuseRegisting消息。

通过向作业管理器发送带有相应的作业图的SubmitJob消息,作业被提交给作业管理器。在接收到JobGraph之后,JobManager从JobGraph中创建一个ExecutionGraph,它用作分布式执行的逻辑表示。ExecutionGraph包含有关必须部署到TaskManager才能执行的任务的信息。

JobManager的调度器负责分配TaskManager上的可用的执行slot。在TaskManager上分配执行slot之后,将带有执行任务所需的所有信息的SubmitTask消息发送到相应的TaskManager。TaskOperationResult确认任务部署成功。部署并运行提交的作业的源之后,作业提交也被认为是成功的。JobManager通过发送带有相应作业ID的成功消息向JobClient通知该状态。

在TaskManagers上运行的单个任务的状态更新通过UpdateTaskExecutionState消息发送回JobManager。通过这些更新消息,可以更新ExecutionGraph以反映执行的当前状态。

JobManager还充当数据源的输入分割分配器。它负责将工作分布到所有TaskManager,以便尽可能保留数据位置。为了动态平衡负载,任务在完成对旧输入的处理之后请求新的输入分离。这个请求是通过向JobManager发送RequestNextInputSplit来实现的。JobManager响应NextInputSplit消息。如果没有更多的输入分隔,则消息中包含的输入分隔为空。

任务被懒部署到任务管理器。这意味着,只在其生产者之一完成生产某些数据之后才部署消耗数据的任务。一旦生产者这样做了,它将向JobManager发送ScheduleOrUpdateConsumers消息。这些消息表示消费者现在可以读取新产生的数据。如果消耗任务尚未运行,则会将其部署到TaskManager。

5.JobClient

JobClient表示分布式系统的面向用户的组件。它用于与JobManager通信,因此它负责提交Flink作业、查询提交的作业的状态以及接收当前运行的作业的状态消息。

JobClient也是通过消息进行通信的actor。有两个与作业提交相关的消息:SubmitJobDetached和SubmitJobWait。第一条消息提交作业,并且从接收任何状态消息和最终作业结果中去寄存器。如果希望以fire和forget 方式将作业提交到Flink集群,则分离模式非常有用。

SubmitJobWait消息向JobManager提交作业,并注册以接收该作业的状态消息。在内部,这是通过生成助手角色来完成的,助手角色用作状态消息的接收器。一旦作业终止,JobManager将带有持续时间和累加器结果的JobResultSuccess发送到派生的助手actor 。在接收到此消息后,助手actor 将消息转发给客户端,客户端最初发出SubmitJobWait消息,然后终止。

6. 异步消息与同步消息

在可能的情况下,Flink尝试使用异步消息并将响应作为futures处理。futures和少数现有的阻塞调用有一个超时,此后操作被认为是失败的。这可以防止系统在消息丢失或分布式组件崩溃时陷入死锁。但是,如果碰巧有一个非常大的集群或较慢的网络,则可能错误地触发超时。因此,可以通过配置中的“akka.ask.timeout”指定这些操作的超时。

在actor可以和另一个actor通信之前,它必须检索ActorRef。查找此操作还需要超时。为了在actor未启动时使系统快速失败,将查找超时设置为比常规超时更小的值。如果遇到查找超时,可以通过配置中的“akka.lookup.timeout”增加查找时间。

Akka的另一个特点是它设置了最大消息大小的限制。这是因为它保留了相同大小的串行化缓冲区,并且不想浪费内存。如果因为消息超出了最大大小而遇到传输错误,则可以通过配置中的“akka.framesize”增加帧大小。

7. 故障检测

分布式系统的故障检测对于系统的鲁棒性至关重要。在集群上运行时,经常会发生某些组件失败或无法再访问的情况。这种故障的原因是多态的,可以从硬件故障到网络中断。一个健壮的分布式系统应该能够检测出故障组件并从中恢复。

Flink使用Akka的DeathWatch机制检测出故障组件。DeathWatch允许actor观看其他actor,即使它们没有其他actor监督到,甚至存活在在不同的Actor系统里。一旦被观察到的actor挂掉或无法再访问,将向观察的actor发送终止消息。因此,在接收到这样的消息后,系统可以采取措施来处理它。在内部,DeathWatch被实现为心跳和故障检测器,该故障检测器基于心跳间隔、心跳暂停和故障阈值来估计actor何时可能挂掉。可以通过在配置中设置“akka.watch.heartbeat.interval”值来控制心跳间隔。可接受的心跳暂停可以通过“akka.watch.heartbeat.pause”指定。心跳暂停应该是心跳间隔的倍数,否则丢失的心跳将直接触发DeathWatch。故障阈值可以通过“akka.watch.threshold”指定,并且它有效地控制故障检测器的灵敏度。

在Flink中,JobManager监视所有已注册的任务管理器,TaskManager监视JobManager。这样,两个组件都知道其他组件何时不再可访问。JobManager通过将相应的TaskManager标记为dead来作出反应,从而防止将来任务部署到它。此外,它会使当前在这个任务管理器上运行的所有任务失败,并重新安排它们在另一个TaskManager上的执行。如果TaskManager仅仅因为临时连接丢失而被标记为已死,那么一旦重新建立了连接,它就可以简单地在JobManager中重新注册自己。

任务管理器还监视JobManager。此监视允许TaskManager在检测到失败的JobManager时通过使当前运行的所有任务失败而进入清洁状态。此外,如果触发的死亡仅由网络拥塞或连接丢失引起,TaskManager将尝试重新连接到JobManager。

8.未来发展

目前,只有三个组件(JobClient、JobManager和TaskManager)作为参与者实现。为了更好地利用并发性同时提高可伸缩性,可以考虑将更多的组件实现为actor。一个有希望的actor可以是ExecutionGraph,其单独的ExecutionVertices或者甚至相关联的Execution对象都可以作为actor实现。这样的细粒度actor模型具有这样的优点,即状态更新可以直接发送到相应的Execution对象。通过这种方式,JobManager将明显地从单点通信中解脱出来。

猜你喜欢

转载自blog.csdn.net/hxcaifly/article/details/84998240