|NO.Z.00076|——————————|BigDataEnd|——|Hadoop&Spark.V02|——|Spark.v02|Spark 原理 源码|Master Worker解析&Spark RPC框架|

一、Master & Worker 解析
### --- Spark RPC 框架

~~~     RPC(Remote Procedure Call)远程过程调用。
~~~     两台服务器ABA服务器上的应用,想要调用B服务器上应用提供的函数/方法,
~~~     由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
~~~     RPC接口。让使用者调用远程请求时,就像调用本地函数
~~~     序列化、反序列化
~~~     网络传输
### --- Spark RPC框架说明

~~~     如果把分布式系统(Hadoop、Spark等)比作一个人,那么RPC可以认为是人体的血液循环系统。
~~~     它将系统中各个不同的组件联系了起来。
~~~     在Spark中,不同组件之间的通信、jar的上传、Shuffle数据的传输、
~~~     Block数据的复制与备份都是基于RPC来实现的,所以说 RPC 是分布式系统的基石毫不为过。
~~~     在 Spark 2.x 之前,RPC框架是借助 Akka 来实现的。Akka 是非常优秀的开源分布式框架。
~~~     弃用 Akka,主要是解决用户的Spark Application 中 Akka 版本和 Spark 内置的 Akka版本冲突的问题。
~~~     Spark 2.X 的 RPC 框架是基于优秀的网络通信框架 Netty 开发的。
~~~     Spark RPC借鉴了Akka的中的设计,它是基于Actor模型,
~~~     各个组件可以认为是一个个独立的实体,各个实体之间通过消息来进行通信。
二、具体各个组件之间的关系图如下:

三、Spark RPC框架
### --- Rpc Env

~~~     RpcEnv是RPC的环境对象,管理着整个 RpcEndpoint 的生命周期,
~~~     其主要功能有:根据name或uri注册endpoints、管理各种消息的处理、停止endpoints。
~~~     其中RpcEnv只能通过RpcEnvFactory创建得到。 
### --- RpcEnv中的核心方法:

~~~     # RpcEndpoint 向 RpcEnv 注册
def setupEndpoint(name: String, endpoint: RpcEndpoint): RpcEndpointRef
~~~     # 根据参数信息,从 RpcEnv 中获得一个远程的RpcEndpoint
def setupEndpointRef(address: RpcAddress, endpointName: String): RpcEndpointRef
四、RpcEndpoint
### --- RpcEndpoint

~~~     RpcEndpoint:表示一个消息通信体,可以接收、发送、处理消息。
~~~     RpcEndpoint的生命周期为:constructor -> onStart -> receive* -> onStop,
~~~     其中:
~~~     onStart在接收任务消息前调用主要用来执行初始化
~~~     receivereceiveAndReply 分别用来接收RpcEndpoint sendask 过来的消息
~~~     receive方法,接收由RpcEndpointRef.send方法发送的消息,
~~~     该类消息不需要进行响应消息(Reply),而只是在RpcEndpoint端进行处理
~~~     receiveAndReply方法,接收由RpcEndpointRef.ask发送的消息,
~~~     RpcEndpoint端处理完消息后,需要给调用RpcEndpointRef.ask的通信端响应消息
~~~     send发送的消息不需要立即处理,ask发送的消息需要立即处理
### --- 源码提取说明

~~~     # 源码提取说明:RpcEndpoint.scala
~~~     # 46行
private[spark] trait RpcEndpoint {

  /**
   * The [[RpcEnv]] that this [[RpcEndpoint]] is registered to.
   * 当前RpcEndpoint所注册的[[RpcEnv]]
   */
  val rpcEnv: RpcEnv

  /**
   * The [[RpcEndpointRef]] of this [[RpcEndpoint]]. `self` will become valid when `onStart` is
   * called. And `self` will become `null` when `onStop` is called.
   * 当前[[RpcEndpoint]]的代理,当`onStart`方法被调用时`self`生效,当`onStop`被调用时,`self`变成null
   *
   * Note: Because before `onStart`, [[RpcEndpoint]] has not yet been registered and there is not
   * valid [[RpcEndpointRef]] for it. So don't call `self` before `onStart` is called.
   * 注意:在`onStart`方法被调用之前,[[RpcEndpoint]]对象还未进行注册,所以就没有有效的
   */
  final def self: RpcEndpointRef = {
    require(rpcEnv != null, "rpcEnv has not been initialized")
    rpcEnv.endpointRef(this)
  }

  /**
   * Process messages from `RpcEndpointRef.send` or `RpcCallContext.reply`. If receiving a
   * unmatched message, `SparkException` will be thrown and sent to `onError`.
   * 用于处理从`RpcEndpointRef.send` 或 `RpcCallContext.reply`接收到的消息。
   * 如果接收到一个不匹配的消息,将会抛出SparkException异常,并发送给`onError`
   *
   * 通过上面的receive方法,接收由RpcEndpointRef.send方法发送的消息,
   * 该类消息不需要进行响应消息(Reply),而只是在RpcEndpoint端进行处理
   */
  def receive: PartialFunction[Any, Unit] = {
    case _ => throw new SparkException(self + " does not implement 'receive'")
  }

  /**
   * Process messages from `RpcEndpointRef.ask`. If receiving a unmatched message,
   * `SparkException` will be thrown and sent to `onError`.
   * 处理来自`RpcEndpointRef.ask`的消息,RpcEndpoint端处理完消息后,需要给调用RpcEndpointRef.ask的通信端返回响应消息
   */
  def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = {
    case _ => context.sendFailure(new SparkException(self + " won't reply anything"))
  }

  /**
   * Invoked when any exception is thrown during handling messages.
   * 在处理消息期间出现异常的话将被调用
   */
  def onError(cause: Throwable): Unit = {
    // By default, throw e and let RpcEnv handle it
    throw cause
  }

  /**
   * Invoked when `remoteAddress` is connected to the current node.
   * 当有远端连接到当前服务器时会被调用
   */
  def onConnected(remoteAddress: RpcAddress): Unit = {
    // By default, do nothing.
  }

  /**
   * Invoked when `remoteAddress` is lost.
   * 当远端与当前服务器断开时,该方法会被调用
   */
  def onDisconnected(remoteAddress: RpcAddress): Unit = {
    // By default, do nothing.
  }

  /**
   * Invoked when some network error happens in the connection between the current node and
   * `remoteAddress`.
   * 当前节点与远端之间的连接发生错误时,该方法将会被调用
   */
  def onNetworkError(cause: Throwable, remoteAddress: RpcAddress): Unit = {
    // By default, do nothing.
  }

  /**
   * Invoked before [[RpcEndpoint]] starts to handle any message.
   * 在 [[RpcEndpoint]] 开始处理消息之前被调用
   */
  def onStart(): Unit = {
    // By default, do nothing.
  }

  /**
   * Invoked when [[RpcEndpoint]] is stopping. `self` will be `null` in this method and you cannot
   * use it to send or ask messages.
   * 当[[RpcEndpoint]]正在停止时,该方法将会被调用
   * `self`将会在该方法中被置位null,因此你不能使用它来发送消息
   */
  def onStop(): Unit = {
    // By default, do nothing.
  }

  /**
   * A convenient method to stop [[RpcEndpoint]].
   */
  final def stop(): Unit = {
    val _self = self
    if (_self != null) {
      rpcEnv.stop(_self)
    }
  }
}
五、RpcEndPointRef
### --- RpcEndPointRef

~~~     RpcEndpointRef 是对远程 RpcEndpoint 的一个引用。
~~~     当需要向一个具体的RpcEndpoint发送消息时,需要获取到该RpcEndpoint的引用,
~~~     然后通过该引用发送消息。
~~~     send 方法发送消息后不等待响应,亦即 Send-and-forget
~~~     ask 方法发送消息后需要等待通信对端给予响应,通过 Future 来异步获取响应结果
### --- 源码提取说明

~~~     # 源码提取说明:RpcEndpointRef.scala
~~~     # 30private[spark] abstract class RpcEndpointRef(conf: SparkConf)
  extends Serializable with Logging {

  private[this] val maxRetries = RpcUtils.numRetries(conf)
  private[this] val retryWaitMs = RpcUtils.retryWaitMs(conf)
  private[this] val defaultAskTimeout = RpcUtils.askRpcTimeout(conf)

  /**
   * return the address for the [[RpcEndpointRef]]
   * 返回[RpcEndpointRef]]的引用的远端服务器地址
   */
  def address: RpcAddress

  def name: String

  /**
   * Sends a one-way asynchronous message. Fire-and-forget semantics.
   * 发送一条单向的异步消息,并且发送消息后不等待响应,亦即Send-and-forget
   */
  def send(message: Any): Unit

  /**
   * Send a message to the corresponding [[RpcEndpoint.receiveAndReply)]] and return a [[Future]] to
   * receive the reply within the specified timeout.
   *
   * This method only sends the message once and never retries.
   * 发送消息给相关的[[RpcEndpoint.receiveAndReply]],并且返回一个 Future,能够在timeout时间内接收回复
   * 该方法只会发送一次消息,失败后不重试
   * 而ask方法发送消息后需要等待通信对端给予响应,通过Future来异步获取响应结果
   */
  def ask[T: ClassTag](message: Any, timeout: RpcTimeout): Future[T]

  /**
   * Send a message to the corresponding [[RpcEndpoint.receiveAndReply)]] and return a [[Future]] to
   * receive the reply within a default timeout.
   *
   * This method only sends the message once and never retries.
   * 发送消息给相关的[[RpcEndpoint.receiveAndReply]],并且返回一个 Future,能够在defaultAskTimeout时间内接收回复
   * 该方法只会发送一次消息,失败后不重试
   * 而ask方法发送消息后需要等待通信对端给予响应,通过Future来异步获取响应结果
   */
  def ask[T: ClassTag](message: Any): Future[T] = ask(message, defaultAskTimeout)

  /**
   * Send a message to the corresponding [[RpcEndpoint.receiveAndReply]] and get its result within a
   * default timeout, throw an exception if this fails.
   *
   * Note: this is a blocking action which may cost a lot of time,  so don't call it in a message
   * loop of [[RpcEndpoint]].
   * 发送消息给相关的[[RpcEndpoint.receiveAndReply)]],并且返回一个Future,能够在defaultAskTimeout时间内接收回复,如果超时则抛出异常
   * 注意:该方法会阻塞当前线程

   * @param message the message to send
   * @tparam T type of the reply message
   * @return the reply message from the corresponding [[RpcEndpoint]]
   */
  def askSync[T: ClassTag](message: Any): T = askSync(message, defaultAskTimeout)

  /**
   * Send a message to the corresponding [[RpcEndpoint.receiveAndReply]] and get its result within a
   * specified timeout, throw an exception if this fails.
   *
   * Note: this is a blocking action which may cost a lot of time, so don't call it in a message
   * loop of [[RpcEndpoint]].
   *
   * @param message the message to send
   * @param timeout the timeout duration
   * @tparam T type of the reply message
   * @return the reply message from the corresponding [[RpcEndpoint]]
   * 发送消息给相关的[[RpcEndpoint.receiveAndReply)]],并且返回一个Future,能够在timeout时间内接收回复,如果超时则抛出异常
   * 注意:该方法会阻塞当前线程
   *
   * @param 发送的消息内容
   * @param 超时时长
   * @tparam 响应消息的类型
   * @return 从[[RpcEndpoint]]端响应的消息内容
   */
  def askSync[T: ClassTag](message: Any, timeout: RpcTimeout): T = {
    val future = ask[T](message, timeout)
    timeout.awaitResult(future)
  }
}
六、其它组件
### --- 其它组件

~~~     Dispatcher,消息分发器。针对RpcEndpoint发送/接收到的消息,分发至对应的指令收件箱/发件箱。
~~~     如果指令接收方是自己存入收件箱,如果指令接收方为非自身端点,则放入发件箱
~~~     Inbox,指令消息收件箱。一个本地端点对应一个收件箱,Dispatcher在每次向Inbox存入消息时,
~~~     将对应EndpointData 加入内部待Receiver Queue中,
~~~     另外Dispatcher创建时会启动一个单独线程进行轮询ReceiverQueue,进行收件箱消息消费
~~~     OutBox,指令消息发件箱。一个远程端点对应一个发件箱,当消息放入Outbox后,
~~~     紧接着将消息通过TransportClient 发送出去
~~~     TransportClient,Netty通信客户端。
~~~     根据OutBox消息的receiver信息,请求对应远程TransportServer
~~~     TransportServer,Netty通信服务端。一个RPC端点一个TransportServer,
~~~     接收远程消息后调用Dispatcher分发消息至对应收发件箱

 
 
 
 
 
 
 
 
 

Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
                                                                                                                                                   ——W.S.Landor

 

 

posted on   yanqi_vip  阅读(22)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示