zoukankan      html  css  js  c++  java
  • Flink 源码(十八):组件通信(一)基本概念

    1 Akka 与 Actor 模型

      Flink 内部节点之间的通信是用 Akka,比如 JobManager 和 TaskManager 之间的通信。而 operator 之间的数据传输是利用 Netty。
      Flink 通过 Akka 进行的分布式通信的实现,在 0.9 版中采用。使用 Akka,所有远程过程调用现在都实现为异步消息。这主要影响组件 JobManager,TaskManager 和 JobClient。将来,甚至有可能将更多的组件转换为参与者,从而允许它们发送和处理异步消息。
      RPC 框架是 Flink 任务运行的基础,Flink 整个 RPC 框架基于 Akka 实现,并对 Akka 中的 ActorSystem、Actor 进行了封装和使用,Flink 整个通信框架的组件主要由 RpcEndpoint、
    RpcService、RpcServer、AkkaInvocationHandler、AkkaRpcActor 等构成。RpcEndpoint 定义了一个 Actor 的 路 径 ; RpcService 提 供 了 启 动 RpcServer 、 执 行 代 码 体 等 方 法 ;RpcServer/AkkaInvocationHandler 提供了与 Actor 通信的接口;AkkaRpcActor 为 Flink 封装的 Actor。下面分析 Flink 底层 RPC 通信框架的实现和相关流程。
      Akka 是一个开发并发、容错可伸缩应用的框架。它是 Actor Model 的一个实现,和Erlang 的并发模型很像。在 Actor 模型中,所有的实体被认为是独立的 actors。actors 和其他actors 通过发送异步消息通信。Actor 模型的强大来自于异步。它也可以显式等待响应,这使得可以执行同步操作。但是,强烈不建议同步消息,因为它们限制了系统的伸缩性。每个 actor有一个邮箱(mailbox),它收到的消息存储在里面。另外,每一个 actor 维护自身单独的状态。
      一个 Actors 网络如下所示:
      每个 actor 是一个单一的线程,不断地从其邮箱中 poll(拉取)消息,并且连续不断地处理。对于已经处理过的消息的结果,actor 可以改变它自身的内部状态或者发送一个新消
    息或者孵化一个新的 actor。尽管单个的 actor 是自然有序的,但一个包含若干个 actor 的系统却是高度并发的并且极具扩展性的。因为那些处理线程是所有 actor 之间共享的。这也是我们为什么不该在 actor 线程里调用可能导致阻塞的“调用”。因为这样的调用可能会阻塞该线程使得他们无法替其他 actor 处理消息。 
    1.1 Actor 系统
      一个 Actor 系统包含了所有存活的 actors。它提供的共享服务包括调度、配置和日志等。Actor 系统同时包含一个线程池,所有 actor 从这里获取线程。
      多 个 Actor 系 统 可 以 在 一 台 机 器 上 共 存 。如果一个 Actor 系 统 通 过RemoteActorRefProvider 启动,它就可以被其他机器上的 Actor 系统发现。Actor 系统能够自动识别消息是发送给本地机器还是远程机器的 Actor 系统。在本地通信的情况下,消息通过共享存储器高效的传输。在远程通信的情况下,消息通过网络栈发送。 
      所有 Actors 都是继承来组织的。每个新创建的 actor 将其创建的 actor 视作父 actor。继承被用来监督。每个父 actor 对自己的子 actor 负责监督。如果在一个子 actor 发生错误,父
    actor 将会收到通知。如果这个父 actor 可以解决这个问题,它就重新启动这个子 actor。如果这个错误父 actor 无法处理,它可以把这个错误传递给自己的父 actor。
      第一个 actor 通过系统创建,由/user 这个 actor 负责监督。详细的 Actor 的继承制度可以参考 https://doc.akka.io//docs/akka/snapshot/general/supervision.html。 
    1.2 Flink 中的 Actors
      Actor 是一个包含状态和行为的容器。actor 线程顺序处理收到的消息。这样就让用户摆脱锁和线程管理的管理,因为一次只有已给线程对一个 actor 有效。但是,必须确保只有这
    个 actor 线程可以处理其内部状态。Actor 的行为由 receive 函数定义,该函数包含收到的消息的处理逻辑。
      Flink 系统由 3 个分布式组件构成:JobClient,JobManager 和 TaskManager。JobClient 从用户处得到 Flink Job,并提交给 JobManager。JobManager 策划这个 job 的执行。首先,它分
    配所需的资源,主要就是 TaskManagers 上要执行的 slot。
      在资源分配之后,JobManager 部署单独的任务到响应的 TaskManager 上。一旦收到一个任务,TaskManager 产生一个线程用来执行这个任务。状态的改变,比如开始计算或者完成计算,将被发送回 JobManager。基于这些状态的更新,JobManager 将引导这个 job 的执行直到完成。一旦一个 job 被执行完,其结果将会被发送回 JobClient。Job 的执行图如下所示: 
    1.3 异步 vs 同步消息
      在任何地方,Flink 尝试使用异步消息和通过 Futures(用来获取异步的响应)来处理响应。Futures 和很少的几个阻塞调用有一个超时时间,以防操作失败。这是为了防止死锁,当
    消息丢失或者分布式组件 crash。但是,如果在一个大集群或者慢网络的情况下,超时可能会使得情况更糟。因此,操作的超时时间可以通过“akka.timeout.timeout”来配置。
      在两个 actor 可以通信之前,需要获取一个 ActorRef。这个操作的查找同样需要一个超时。为了使得系统尽可能快速的失败,如果一个 actor 还没开始,超时时间需要被设置的比
    较小。为了以防经历查询超时,可以通过“akka.lookup.timeout”配置增加查询时间。
      Akka 的另一个特点是限制发送的最大消息大小。原因是它保留了同样数据大小的序列化 buffer 和不想浪费空间。如果你曾经遇到过传输失败,因为消息超过了最大大小,你可以
    增加“akka.framesize”配置来增加大小。

    2 使用 Akka

      Akka 系统的核心 ActorSystem 和 Actor,若需构建一个 Akka 系统,首先需要创建ActorSystem,创建完 ActorSystem 后,可通过其创建 Actor(注意:Akka 不允许直接 new 一
    个 Actor,只能通过 Akka 提供的某些 API 才能创建或查找 Actor,一般会通过ActorSystem#actorOf 和 ActorContext#actorOf 来创建 Actor),另外,我们只能通过 ActorRef
    (Actor 的引用,其对原生的 Actor 实例做了良好的封装,外界不能随意修改其内部状态)来与 Actor 进行通信。如下代码展示了如何配置一个 Akka 系统。
    2.1 Actor 路径
      在 Akka 中,创建的每个 Actor 都有自己的路径,该路径遵循 ActorSystem 的层级结构,大致如下:
    1)本地路径
    在上面代码中,本地 Actor 路径为 akka://sys/user/helloActor
    含义如下:
    ⚫ sys,创建的 ActorSystem 的名字;
    ⚫ user,通过 ActorSystem#actorOf 和 ActorContext#actorOf 方法创建的 Actor 都属于/user 下,与/user 对应的是/system, 其是系统层面创建的,与系统整体行为有关,在开发阶段并不需要对其过多关注
    ⚫ helloActor,我们创建的 HelloActor
    2)远程路径
    在上面代码中,远程 Actor 路径为 akka.tcp://sys@l27.0.0.1:2020/user/remoteActor
    含义如下:
    ⚫ akka.tcp,远程通信方式为 tcp;
    ⚫ sys@127.0.0.1:2020,ActorSystem 名字及远程主机 ip 和端口号。
    ⚫ user,与本地的含义一样
    ⚫ remoteActor,创建的远程 Actor
    2.2 获取 Actor
      若提供了 Actor 的路径,可以通过路径获取到 ActorRef,然后与之通信,代码如下所示: 

    3 与 Actor 通信

      Akka 有两种核心的异步通信方式:tell 和 ask。
    3.1 tell 方式
      当使用 tell 方式时,表示仅仅使用异步方式给某个 Actor 发送消息,无需等待 Actor 的响应结果,并且也不会阻塞后续代码的运行,如: 
    helloActor.tell("hello helloActor", ActorRef.noSender());
      其中:第一个参数为消息,它可以是任何可序列化的数据或对象,第二个参数表示发送者,通常来讲是另外一个 Actor 的引用, ActorRef.noSender()表示无发送者(实际上是一个
    叫做 deadLetters 的 Actor)。 
    3.2 ask 方式
      当我们需要从 Actor 获取响应结果时,可使用 ask 方法,ask 方法会将返回结果包装在scala.concurrent.Future 中,然后通过异步回调获取返回结果。如调用方: 
    HelloActor 处理消息方法的代码大致如下:
    上面主要介绍了 Akka 中的 ActorSystem、Actor,及与 Actor 的通信;Flink 借此构建了其底层通信系统。 
     
     
     
  • 相关阅读:
    npm脚本和package.json
    Vue官方文档笔记(二)
    Vue官方文档笔记
    2019windows上安装Mac OS 10.14过程详细截图
    三次握手四次挥手
    ==和equals的区别
    Stringbuffer和Stringbuilder的区别
    字符串拼接五种常用方法
    什么是线程以及保证线程安全
    什么是线程安全,怎样保证线程安全
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/14502626.html
Copyright © 2011-2022 走看看