回顾
上一节我们讲了SparkContext构造函数做的初始化,会创建TaskScheduler和DAGScheduler两个重要对象,TaskScheduler又会通过StandaloneSchedulerBackend构建Driver和client。这节我们接着看下client是如何进行注册的。
client = new StandaloneAppClient(sc.env.rpcEnv, masters, appDesc, this, conf)
client.start()
创建client
我们进入start()可以看到一个ClientEndpoint,会创建一个内部类ClientEndpoint,里面有个onStart()方法,里面就是和注册有关的内容。
endpoint.set(rpcEnv.setupEndpoint("AppClient", new ClientEndpoint(rpcEnv)))
调用onStart()
不过调用的过程也是有点绕,看的时候本人也是通过onStart()反推回去的。有兴趣的也可以和我一起过一遍。需要从rpcEnv.setupEndpoint(…)开始,spark1.6之后的网络传输使用netty替代了akka,这个方法的实现类也有所体现NettyRpcEnv。
dispatcher.registerRpcEndpoint(name, endpoint)
看方法名也知道和注册有关,dispatcher做的最重要的事就是创建EndpointData
if (endpoints.putIfAbsent(name, new EndpointData(name, endpoint, endpointRef)) != null)
我们看到EndpointData构造函数里就做了一件事,创建一个Inbox,所以还要往下看。
// Inbox为[[RpcEndpoint]]存储消息并以线程安全的方式向其发送消息的收件箱。
val inbox = new Inbox(ref, endpoint)
在构造方法里,终于我们看到一个比较熟悉点的名字了OnStart,messages添加了这条消息,看解析我就能清楚这条OnStart类型的信息将会被第一个处理
// OnStart should be the first message to process
inbox.synchronized {
messages.add(OnStart)
}
继续往下看会发现一个process(…)方法处理各种消息,而dispatcher中有个异步线程会不断调用process()处理这些消息。
def process(dispatcher: Dispatcher): Unit = {
...
case OnStart =>
// client中的onStart()就是在这里被调用
endpoint.onStart()
注册application
让我们回到ClientEndpoint的onStart(),调用了registerWithMaster()
方法。
registerMasterFutures.set(tryRegisterAllMasters())
以异步方式向所有主机注册,并返回一个JFuture数组。
private def tryRegisterAllMasters(): Array[JFuture[_]] = {
for (masterAddress <- masterRpcAddresses) yield {
// 发往线程池异步提交这个任务
registerMasterThreadPool.submit(new Runnable {
override def run(): Unit = try {
if (registered.get) {
return
}
logInfo("Connecting to master " + masterAddress.toSparkURL + "...")
val masterRef = rpcEnv.setupEndpointRef(masterAddress, Master.ENDPOINT_NAME)
// 发送application注册消息
masterRef.send(RegisterApplication(appDescription, self))
} catch {
case ie: InterruptedException => // Cancelled
case NonFatal(e) => logWarning(s"Failed to connect to master $masterAddress", e)
}
})
}
}
可以看出spark内部组件的交互主要就是通过各种类型消息进行的,client注册完成,master收到消息后,就会进行相关的资源分配,也就是Master的receive()方法有关的处理。
不足之处还望大家指正。下节我们再来分析里面的具体操作,也就算spark的资源分配。