Spark 提交任务 源码解析
spark-submit
exec "${SPARK_HOME}"/bin/spark-class org.apache.spark.deploy.SparkSubmit "$@"
spark-class
在这里相当于进行
bin/java org.apache.spark.deploy.SparkSubmit
启动java虚拟机,启动一个进程 进程名叫作 SparkSubmit
sparksubmit
--override def main方法
val submit = new SparkSubmit(){...}
--执行提交参数
submit.doSubmit(args)
--解析参数
val appArgs = parseArguments(args)
--创建parkSubmitArguments类
new SparkSubmitArguments(args)
--类的初始化
// 从命令行设置参数
parse(args.asJava)
·--通过解析用户选项来填充值
handle(调用SparksubmitArguments重写的此函数)
(将命令行里的参数值--class,--master......赋值给SparksubmitArguments)
--
appArgs.action match {
[action:点进去搜action会发现它在def loadEnvironmentArguments()这个方法中,且action = Option(action).getOrElse(SUBMIT),意思是如果action为null就给他赋值submit]
case SparkSubmitAction.SUBMIT => submit(appArgs, uninitLog)
--submit点进来
def doRunMain(){
if (args.proxyUser != null){......}//这个参数proxyUser我们的命令行里没有这个值,所以为空
else {runMain(args, uninitLog)}
--runMain(args, uninitLog)函数中
//我用的yarn集群,childMainClass=YARN_CLUSTER_SUBMIT_CLASS=org.apache.spark.deploy.yarn.YarnClusterApplication
prepareSubmitEnvironment(args)//准备提交环境参数,其返回值类型是(childArgs, childClasspath, sparkConf, childMainClass)
--点进去prepareSubmitEnvironment(args),搜索childMainClass发现
if (args.isStandaloneCluster) {childMainClass=......}
if (isYarnCluster) {childMainClass=......}
if (isMesosCluster) {childMainClass=......}
if (isKubernetesCluster){childMainClass=......}
val loader = getSubmitClassLoader(sparkConf)//获取提交的类加载器
for (jar <- childClasspath) {
addJarToClasspath(jar, loader)//将spark环境的jar包传给yarn
}
//根据childMainClass字符串来获取类的对象,childMainClass由prepareSubmitEnvironment(args)提供
mainClass = Utils.classForName(childMainClass)//通过反射得到childMainClass的信息
//获取SparkApplication
val app: SparkApplication = if (classOf[SparkApplication].isAssignableFrom(mainClass)) {
//判断当前的mainclass是否为SparkApplication的子类
//如果是就构建mainclass
mainClass.getConstructor().newInstance().asInstanceOf[SparkApplication]
} else {
//如果不是就构建JavaMainApplication,把mainclass传进去
//点进去JavaMainApplication,你会发现他是继承SparkApplication的
new JavaMainApplication(mainClass)
}
app.start(childArgs.toArray, sparkConf)
//在JavaMainApplication,或者SparkApplication点进去 搜索start
--start方法
//在mainclass中取main方法
val mainMethod = klass.getMethod("main", new Array[String](0).getClass)
//检查main方法是不是静态的,如果不是就抛异常
if (!Modifier.isStatic(mainMethod.getModifiers))
//调用main方法
mainMethod.invoke(null, args)
}
//判断当前模式是否为standalone模式
if (args.isStandaloneCluster && args.useRest)
else
调用defRunMain()方法
case SparkSubmitAction.KILL => kill(appArgs)
case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs)
case SparkSubmitAction.PRINT_VERSION => printVersion()
}
org.apache.spark.deploy.yarn.YarnClusterApplication
--start
//找到org.apache.spark.deploy.yarn.YarnClusterApplication
new Client(new ClientArguments(args), conf, null).run()
[ ClientArguments(args)将参数赋值给自己的属性例如:--class给了userclass]
--Client点进来
private val yarnClient = YarnClient.createYarnClient
--createYarnClient点进来
YarnClient client = new YarnClientImpl()
--YarnClientImpl点进来
//发现他在这里有一个RMClient属性
protected ApplicationClientProtocol rmClient;
--点击run
submitApplication()//提交应用
--点进去submitApplication()
//连接后台
launcherBackend.connect()
//初始化yarn客户端
yarnClient.init(hadoopConf)
//启动yarn客户端
yarnClient.start()
// Get a new application from our RM 向RM申请一个applicationID
val newApp = yarnClient.createApplication()
val newAppResponse = newApp.getNewApplicationResponse()
appId = newAppResponse.getApplicationId()
// Set up the appropriate contexts to launch our AM
//当前容器环境
val containerContext = createContainerLaunchContext(newAppResponse)
--点进去createContainerLaunchContext()发现他在配置虚拟机参数,并传送了一个COmmand
val commands = prefixEnv ++
Seq(Environment.JAVA_HOME.$$() + "/bin/java", "-server") ++......
最后command被拼接成:bin/java org.apache.saprk.doploy.yarn.ApplicationMaster
applicationmaster源码分析见下一小节
//当前应用环境
val appContext = createApplicationSubmissionContext(newApp, containerContext)
//提交并监视Application
yarnClient.submitApplication(appContext)
--点进submitApplication
--ctrl+h点进YarnClientImpl 看第253行
//向rm提交请求
rmClient.submitApplication(request);
ApplicationMaster
--main函数
//处理参数
val amArgs = new ApplicationMasterArguments(args)
--点进来
//解析参数 --class=>userclass=>你要运行的jar包
parseArgs(args.toList)
//创建AM
val yarnConf = new YarnConfiguration(SparkHadoopUtil.newConfiguration(sparkConf))
master = new ApplicationMaster(amArgs, sparkConf, yarnConf)
--点进ApplicationMaster
//发现他创建了
private val client = new YarnRMClient()
--点进YarnRMClient
//发现一个参数AMRMClient它负责am与rm之间的通信
private var amClient: AMRMClient[ContainerRequest] = _
//am.run
override def run(): Unit = System.exit(master.run())
--点进run方法
//如果是集群模式,跑Driver
if (isClusterMode) {
runDriver()
} else {
runExecutorLauncher()
}
--点进runDriver()方法
//启动用户的应用程序,就是我们的--class后面的jar包
userClassThread = startUserApplication
--点进startUserApplication
//通过类加载器获取我们程序的主函数
val mainMethod = userClassLoader.loadClass(args.userClass)
.getMethod("main", classOf[Array[String]])
//发现他创建了一个线程,这个线程叫做Driver
val userThread = new Thread {
//run函数中
run(){
//判断mainMethod是否为静态,是静态就调用,不是静态就报错
if (!Modifier.isStatic(mainMethod.getModifiers)) {......}
else{......}
......
}
}
userThread.setContextClassLoader(userClassLoader)
userThread.setName("Driver")
userThread.start()
......
//等待sparkcontext的创建,如果sc没有被创建,那么当前线程会被阻塞
val sc = ThreadUtils.awaitResult(sparkContextPromise.future,
Duration(totalWaitTime, TimeUnit.MILLISECONDS))
//sc不为空时
if (sc != null) {
//创建rpc通信环境
val rpcEnv = sc.env.rpcEnv
......
//注册AM
registerAM(host, port, userConf, sc.ui.map(_.webUrl), appAttemptId)
//返回可用资源列表
createAllocator(driverRef, userConf, rpcEnv, appAttemptId, distCacheConf)
--点进createAllocator
//拿到可用资源
allocator = client.createAllocator(......)
//分配资源
allocator.allocateResources()
handleAllocatedContainers(allocatedContainers.asScala)
--点进handleAllocatedContainers(allocatedContainers.asScala)
//发现这个函数会根据节点的位置,选择自己最需要的资源(根据拓扑距离......)
//运行分配的容器
runAllocatedContainers(containersToUse)
--点进runAllocatedContainers(containersToUse)
//发现这个函数的作用是:在分配的容器中启动executor
//创建executorRunnable
new ExecutorRunnable(......)
//执行ExecutorRunnable.run
--点进run方法
//发现他创建了NMClient,用以与NM联系,并启动NM的容器
nmClient = NMClient.createNMClient()
nmClient.init(conf)
nmClient.start()
startContainer()
--点进startContainer()
//发现准备指令
val commands = prepareCommand()
--点击prepareCommand
//发现其commands = /bin/java org.apache.spark.executor.YarnCoarseGrainedExecutorBackend
//将准备指令发给ctx
ctx.setCommands(commands.asJava)
//nmClient发送指令给NM
nmClient.startContainer(container.get, ctx)
}
org.apache.spark.executor.YarnCoarseGrainedExecutorBackend
--main方法
//发现run方法,第一个参数是后台参数,第二个参数是创建的通信终端
CoarseGrainedExecutorBackend.run(backendArgs, createFn)
--run方法点进来
//fetcher通过rpc通信获取值
val fetcher = RpcEnv.create(......)
//fetcher通过rpc与driver获得联系
driver = fetcher.setupEndpointRefByURI(arguments.driverUrl)
//设置终端
env.rpcEnv.setupEndpoint("Executor",
backendCreateFn(env.rpcEnv, arguments, env, cfg.resourceProfile))
CoarseGrainedExecutorBackend
--onStart
//向Driver报告,当前Executor准备好了
--driver.ask( RegisterExecutor )
至此任务提交流程完毕,剩余的是任务的调度。