Spark 提交任务 源码解析

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 )
		
		
至此任务提交流程完毕,剩余的是任务的调度。
		
	
	
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值