SparkSubmit 提交作业源码流程及作业中 driver、client、 executor的创建
源码基于1.5.1版本,流程是自己阅读源码加网上前人的文章学习总结的,可能存在差错,恳请指正。
大体流程说明: (后续作补充修改)
1、通过 sparkSubmit 命令提交执行SparkSubmit的main函数,
2、在SparkSubmit的main函数中调用 prepareSubmitEnvironment 方法,这个方法用于解析当前用户作业提交命令中包含的集群管理器和Driver部署模式,以及命令参数,对环境进行解析
3、环境解析完成后,执行runMain()函数;因为standalone-cluster模式,master以spark开头并且deploy-mode为cluster,因此,mainClass是org.apache.spark.deploy.Client
4、进入到org.apache.spark.deploy.Client的main函数。Client类用于在Standalone-Cluster模式下启动和停止Driver
5、在org.apache.spark.deploy.Client的main函数中创建 ClientEndpoint ,这是一个Akka的Actor,定义于org.apache.spark.deploy.Client 类中
6、ClientEndpoint 执行它的 onStart 方法,主要工作是封装Driver信息,给Master发送 RequestSubmitDriver 请求,请求参数是 DriverDescription
命令提交job后 调用 SparkSubmit.main()
SparkSubmit.main()
=> submit(args: SparkSubmitArguments)
{
val (childArgs, childClasspath, sysProps, childMainClass) = prepareSubmitEnvironment() // 获取系统参数 mainClass 等
{
if (args.isStandaloneCluster) { // 如果是 Standalone 模式
if (args.useRest) { // 如果是 rest 提交
childMainClass = "org.apache.spark.deploy.rest.RestSubmissionClient"
}else{ // 不是 rest 提交
childMainClass = "org.apache.spark.deploy.Client"
}
}
}
runMain()
{
mainClass = Utils.classForName(childMainClass)
val mainMethod = mainClass.getMethod("main", new Array[String](0).getClass)
mainMethod.invoke(null, childArgs.toArray) // 调用 childMainClass 的 main 方法
}
}
在org.apache.spark.deploy.Client 内
org.apache.spark.deploy.Client
{
main()
{
val rpcEnv = RpcEnv.create("driverClient", ...)
val masterEndpoints = driverArgs.masters.map(RpcAddress.fromSparkURL).map(rpcEnv.setupEndpointRef(Master.SYSTEM_NAME, _, Master.ENDPOINT_NAME))
// 创建 ClientEndpoint ,即 Client 端, 随即会调用 Client.ClientEndpoint.onStart() 向Master发送 RequestSubmitDriver 请求注册启动 driver
rpcEnv.setupEndpoint("client", new ClientEndpoint(rpcEnv, driverArgs, masterEndpoints, conf))
}
}
new ClientEndpoint(..)后会自动调用Client.ClientEndpoint中onStart() 方法,方法后流程如下:
=> 向 Master 发送 RequestSubmitDriver 消息
=> Master 调 receiveAndReply()
=> Master 构建 driver 描述,调 schedule() 向 Worker 发送 launchDriver 消息 ; (这里不执行 excutor 的建立)
=> Worker 启动 driver 进程, process = Some(command.start())
driver 进程内:
=> driver 进程内执行 application 中main方法,即执行所编写的代码
=> new SparkContext()
=> backend = new SparkDeploySchedulerBackend()
=> 创建 taskScheduler 、 dagScheduler
=> taskScheduler.start => backend.start()
{
driverEndpoint = rpcEnv.setupEndpoint()
client = new AppClient() => client.start()
{
new ClientEndpoint() 其 onStart()方法 向 Master 发送 RegisterApplication 请求
=> Master 端 receive() 执行 schedule()
=> schedule() 内 启动各 Excutor (这里不执行 driver 的通知建立)
}
}