Spark RPC模块架构图
Spark RPC是按照MailBox的设计思路来实现的,为了能够更直观地表达RPC的设计,我们先从RPC架构图来看,如下图所示:
该图主要描述了从客户端向服务器端发送远程消息和在服务器端发送本地消息的过程,该过程中显示了消息的流动及格式转换,还有各个组件之间的分工协作。
组件介绍
核心组件
Spark RPC通信主要有RpcEnv、RpcEndpoint、RpcEndpointRef这三个核心类。
RpcEndpoint
该类定义了RPC通信过程中的服务器端对象,除了具有管理一组RpcEndpoint生命周期的操作
(constructor -> onStart -> receive* -> onStop)
,并给出了通信过程中RpcEndpoint所具有的基于事件驱动的行为(连接、断开、网络异常),实际上对于Spark框架来说主要是接收消息并处理。private[spark] trait RpcEndpoint { /** * 当前RpcEndpoint所注册的[[RpcEnv]] */ val rpcEnv: RpcEnv /** * 当前[[RpcEndpoint]]的代理,当`onStart`方法被调用时`self`生效,当`onStop`被调用时,`self`变成null。 * 注意:在`onStart`方法被调用之前,[[RpcEndpoint]]对象还未进行注册,所以就没有有效的[[RpcEndpointRef]]。 */ final def self: RpcEndpointRef = { require(rpcEnv != null, "rpcEnv has not been initialized") rpcEnv.endpointRef(this) } /** * 用于处理从`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'") } /** * 处理来自`RpcEndpointRef.ask`的消息,RpcEndpoint端处理完消息后,需要给调用RpcEndpointRef.ask的通信端返回响应消息。 */ def receiveAndReply(context: RpcCallContext): PartialFunction[Any, Unit] = { case _ => context.sendFailure(new SparkException(self + " won't reply anything")) } /** * 在处理消息期间出现异常的话将被调用 */ def onError(cause: Throwable): Unit = { // By default, throw e and let RpcEnv handle it throw cause } /** * 当有远端连接到当前服务器时会被调用 */ def onConnected(remoteAddress: RpcAddress): Unit = { // By default, do nothing. } /** * 当远端与当前服务器断开时,该方法会被调用 */ def onDisconnected(remoteAddress: RpcAddress): Unit = { // By default, do nothing. } /** * 当前节点与远端之间的连接发生错误时,该方法将会被调用 */ def onNetworkError(cause: Throwable, remoteAddress: RpcAddress): Unit = { // By default, do nothing. } /** * 在 [[RpcEndpoint]] 开始处理消息之前被调用 */ def onStart(): Unit = { // By default, do nothing. } /** * 当[[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是一个对RpcEndpoint的远程引用对象,通过它可以向远程的RpcEndpoint端发送消息以进行通信。RpcEndpointRef特质的定义,代码如下所示:
private[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) /** * 返回[RpcEndpointRef]]的引用的远端服务器地址 */ def address: RpcAddress def name: String /** * 发送一条单向的异步消息,并且发送消息后不等待响应,亦即Send-and-forget。 */ def send(message: Any): Unit /** * 发送消息给相关的[[RpcEndpoint.receiveAndReply]],并且返回一个 Future,能够在timeout时间内接收回复。 * 该方法只会发送一次消息,失败后不重试。 * 而ask方法发送消息后需要等待通信对端给予响应