作为分布式系统的spark rpc通信是一个很重要的课题。在spark2.3.3版本中rpc框架已经由akka替换成netty实现。接下来我们先介绍一下spark rpc中的组件,然后根据sparkcontext初始化过程来分析rpc组件如何初始化和使用原理。
在spark中定义transportclient和transportserver来封装netty的channel和handler,定义box来封装bytebuf,所以spark rpc有两个重要过程初始化和通信。
一、RpcEnv初始化
sparkcore所有的初始化都是从sparkcontext开始,如下图,transportclient的初始化也是在sparkContext中。
这里的RpcEnv是一个抽象类,真正实现类是NettyRpcEnv。在NettyRpcEnv中包含了RPC通信的所有操作方法,当然也有创建transportclient所需要的参数以及createClient方法。
private[netty] class NettyRpcEnv(
val conf: SparkConf,
javaSerializerInstance: JavaSerializerInstance,
host: String,
securityManager: SecurityManager,
numUsableCores: Int) extends RpcEnv(conf) with Logging {
private[netty] val transportConf = SparkTransportConf.fromSparkConf(
conf.clone.set("spark.rpc.io.numConnectionsPerPeer", "1"),
"rpc",
conf.getInt("spark.rpc.io.threads", 0))
private val dispatcher: Dispatcher = new Dispatcher(this, numUsableCores)
private val streamManager = new NettyStreamManager(this)
private val transportContext = new TransportContext(transportConf,
new NettyRpcHandler(dispatcher, this, streamManager))
private def createClientBootstraps(): java.util.List[TransportClientBootstrap] = {
if (securityManager.isAuthenticationEnabled()) {
java.util.Arrays.asList(new AuthClientBootstrap(transportConf,
securityManager.getSaslUser(), securityManager))
} else {
java.util.Collections.emptyList[TransportClientBootstrap]
}
}
private val clientFactory = transportContext.createClientFactory(createClientBootstraps())
@volatile private var fileDownloadFactory: TransportClientFactory = _
val timeoutScheduler = ThreadUtils.newDaemonSingleThreadScheduledExecutor("netty-rpc-env-timeout")
@volatile private var server: TransportServer = _
private val stopped = new AtomicBoolean(false)
private val outboxes = new ConcurrentHasMp[RpcAddress, Outbox]()
先来看一段NettyRpcEnv的参数初始化代码(有删减)。这里的初始化参数都有很重要的作用:
-
- transportConf :通信配置参数
- streamManager :流管理器
- transportContext :通信上下文
- dispatcher:netty handler中实际高性能异步处理消息的组件,从代码中可以看到disatcher已经封装到了nettyRpcHandler中,后面会重点讲这个的作用
- clientFactory :创建transportclient工厂在传参的时候有一个createClientBootstraps方法,这个其实是在创建client的时候做一些补充handler比如权限校验
- outboxes :需要传递的消息盒子
二、Dispatcher初始化
在NettyRpcEnv对象创建的时候会直接初始化dispatcher。dispatcher里面有一个内部类用来封装rpcEndpoint、rpcEndpointRef、inbox的EndpointData。RpcEndpoint是消息处理终端属于服务端,RpcEndpointRes属于客户端代表消息终端引用。其中存储的消息是inbox和outbox这里面存储了一个message序列代表所有发送和收到的消息。
private[netty] class Dispatcher(nettyEnv: NettyRpcEnv, numUsableCores: Int) extends Logging {
private class EndpointData(
val name: String,
val endpoint: RpcEndpoint,
val ref: NettyRpcEndpointRef) {
val inbox = new Inbox(ref, endpoint)
}
private val endpoints: ConcurrentMap[String, EndpointData] =
new ConcurrentHashMap[String, EndpointData]
private val endpointRefs: ConcurrentMap[RpcEndpoint, RpcEndpointRef] =
new ConcurrentHashMap[RpcEndpoint, RpcEndpointRef]
// Track the receivers whose inboxes may contain messages.
priva