ApplicationMaster源码分析

ApplicationMaster源码分析

我们从sparksubmit的源码中发现,当创建rmclient之后,我们在nm中启动了一个java进程applicationmaster

val amClass =
  if (isClusterMode) {
    Utils.classForName("org.apache.spark.deploy.yarn.ApplicationMaster").getName
  } else {
    Utils.classForName("org.apache.spark.deploy.yarn.ExecutorLauncher").getName
  }

然后,我们现在就开始,看看启动applicationmaster的时候发生了什么

def main(args: Array[String]): Unit = {
    // 解析参数
    // --class=》userClass =》 SparkPi()
	val amArgs = new ApplicationMasterArguments(args)
    
    //创建了一个ApplicationMaster对象
    val yarnConf = new YarnConfiguration(SparkHadoopUtil.newConfiguration(sparkConf))
    master = new ApplicationMaster(amArgs, sparkConf, yarnConf)
    
    //调用ApplicationMaster对象的run方法
    ugi.doAs(new PrivilegedExceptionAction[Unit]() {
      override def run(): Unit = System.exit(master.run())
    })
    
}

1.ApplicationMaster.run()

//如果是集群模式则runDriver() 如果是client模式则runExecutorLauncher()
if (isClusterMode) {
  runDriver()
} else {
  runExecutorLauncher()
}

2.runDriver()

-- userClassThread = startUserApplication()
	//获取需要执行的程序的main方法,这里只sparkpi的main方法
	-- userClassLoader.loadClass(args.userClass).getMethod("main",classOf[Array[String]])
	//创建了一个用户线程
	-- new Thread().start()
		-- run()
		//执行main方法 --wordcount 而我们spark程序都会去new一个sparkcontext() 好了上下文在这儿就被创建了
		-- mainMethod.invoke(null, userArgs.toArray)
		    //但是在创建sparkcountext是异步的,我们下面的程序已经被阻塞了,所以sparkcountext创建时就会去唤醒下面被阻塞的线程
			-- ApplicationMaster.sparkContextInitialized(sc)
    		-- super.postStartHook()
				//创建好后就把用户线程阻塞,让主线程执行
				-- waitBackendReady()




//阻塞,等待应用程序上下文被创建 
-- val sc = ThreadUtils.awaitResult()
// 注册 在applicationMaster里创建了一个RM的client 然后向RM注册一个任务
-- registerAM(host, port, userConf, sc.ui.map(_.webUrl), appAttemptId)
   -- client.register(host, port, yarnConf, _sparkConf, uiAddress, historyAddress)
      -- private val client = new YarnRMClient()
// 资源管理 
-- createAllocator(driverRef, userConf, rpcEnv, appAttemptId, distCacheConf)
	//申请资源
	-- allocator =  client.createAllocator()
	//资源分配
	-- allocator.allocateResources()
		// 轮询资源管理器。如果没有待处理的容器请求,这会兼作心跳。
       --val allocateResponse = amClient.allocate(progressIndicator)
		// 通过Response拿到容器列表
       --val allocatedContainers = allocateResponse.getAllocatedContainers()
		// 处理分配的容器 由于 YARN 分配协议的工作方式,某些健康的竞争条件可能导致 YARN 授予我们不再需要的容器。在这种情况下,我们释放它们。
       --handleAllocatedContainers(allocatedContainers.asScala)
			// 在分配的容器中启动执行程序
			-- runAllocatedContainers()
				// 根据需要的Executors去启动container ,这里就发现Executor 和 container是一一对应的,直到把需要的container循环创建完
				--for (container <- containersToUse) 
					if (runningExecutors.size() < targetNumExecutors) 
        				numExecutorsStarting.incrementAndGet()
        				if (launchContainers) {
          					launcherPool.execute(() => {
            					try {
              						new ExecutorRunnable(
                						Some(container)........ 
                                  	).run()
                                    // 创建nmClient 去连接 Container所在的nm
                                    --  nmClient = NMClient.createNMClient()
    								--	nmClient.init(conf)
    								--	nmClient.start()
                                    // 启动容器
    								--	startContainer()
                                    	// 准备java指令 /bin/java org.apache.spark.executor.YarnCoarseGrainedExecutorBackend
                                    	-- prepareCommand()
                                    	// 在Container中执行Command
                                        -- nmClient.startContainer(container.get, ctx)
            		
// 恢复用户线程的执行
-- resumeDriver()
// 执行完了回到主程序
-- userClassThread.join()
posted @ 2022-08-30 10:00  chief_y  阅读(67)  评论(0)    收藏  举报