一、主线NettyRpcEnv
上一篇文章,我们分析了Spark Netty在数据传输层相关的实现,及Transport命令相关的类,包括TransportConf、TransportContext、TransportServer、TransportClientFactory、TransportChannelHandler。这些介绍的主要类,均在NettyRpcEnv中创建了成员变量。
在本文,我们将分析NettyRpcEnv中对上述类的包装及使用,即如何实现RPC
1、Netty Rpc总体流程
序号①为一个本地发送消息的特例,表示通过NettyRpcEndpointRef的ask和send方法,向本地节点的RpcEndpoint发送消息,由于是同一节点,直接调用Dispatcher的postLocalMessage或postOnewayMessage方法,将消息放入EndpointData内部的Inbox的Message列表中。Message-Loop线程最后处理消息,并将消息发送给对应的Endpoint处理。
序号②为一个向远端发送消息的流程,表示通过NettyRpcEndpointRed的ask和send方法,向远端节点的RpcEndpoint发送消息,这种情况下,消息先被封装为OutboxMessage,然后放入到远端RPCEndpoint地址对应的OutBox的messages列表中。
序号③表示每个Outbox的drainOutbox方法通过循环,不断从messages列表中取得OutboxMessage。
序号④表示每个Outbox的drainOutbox方法,使用内部的TransportClient向远端的NettyRpcEnv发送序号③所取得的OutboxMessage
序号⑤表示序号④发出的请求在与远端NettyRpcEnv的TransportServer建立了连接后(即launchConnectTask()创建TransportClient),请求消息首先结果Netty管道的处理,然后经过NettyRpccHandler的处理,最后NettyRpcHandler的receive方法会调用Dispatcher的postRemoteMessage或postOneWayMessage方法,将消息放入EndpointData内部的Inbox的messages列表中,MessageLoop线程最后处理消息,并将消息发送给对应的RpcEndpoint处理。
2、创建NettyRpcEnv
NettyRpcEnv是一个主线类,通过它可以分析:
- Client数据调用postToOutbox()发送到outboxes进行分发
- Server端TransportChannelHandler接收到数据,调用NettyRpcHandler的receive()方法,将数据分发到dispatcher中。
- NettyRpcEnv既有Server相关变量和接口函数,也有Client相关变量和接口函数,一些读者在初步接触时,容易混淆。
- 事实上NettyRpcEnv既存在于driver创建的SparkEnv中,也存在于executor创建的SparkEnv中,存在于driver时,会默认创建TransportServer,这样就好理解些了。
上图中标注①,在executor和driver中均创建了NettyRpcEnv,即driver也可以使用client的相关特性,例如向其它远端server发送消息,这里只做了解。
private[rpc] class NettyRpcEnvFactory extends RpcEnvFactory with Logging {
def create(config: RpcEnvConfig): RpcEnv = {
val sparkConf = config.conf
val javaSerializerInstance =
new JavaSerializer(sparkConf).newInstance().asInstanceOf[JavaSerializerInstance]
val nettyEnv =
new NettyRpcEnv(sparkConf, javaSerializerInstance, config.advertiseAddress,
config.securityManager, config.numUsableCores)
if (!config.clientMode) {
val startNettyRpcEnv: Int => (NettyRpcEnv, Int) = { actualPort =>
nettyEnv.startServer(config.bindAddress, actualPort)
(nettyEnv, nettyEnv.address.port)
}
try {
Utils.startServiceOnPort(config.port, startNettyRpcEnv, sparkConf, config.name)._1
} catch {
case NonFatal(e) =>
nettyEnv.shutdown()
throw e
}
}
nettyEnv
}
}
3、NettyRpcEnv成员变量及方法分类介绍
NettyRpcEnv具有server、client相关特性,按以下图示,进行分类说明:
- 图中上部分为Server相关实现,包括streamManager、transportServer、dispatcher
- 图中下部分为Client相关实现,包括clientFactory、outboxes、clientConnectionExecutor
二、client发送数据及outboxes
1、NettyRpcEndpointRef发送数据接口
NettyRpcEndpointRef提供了send()、ask()方法,用于向server发送单向消息和需要回执的RPC消息,其内部实际上是调用NettyRpc对应的接口:
private[netty] class NettyRpcEndpointRef(
private val conf: SparkConf,
private val endpointAddress: RpcEndpointAddress,
private var nettyEnv: NettyRpcEnv) extends Rp