spark源码解析之二、计算资源准备

spark运行流程分为资源环境准备和任务提交运行两个步骤,两个步骤交叉进行,当前以资源准备为主线进行源码分析。
另一条线可以参考:spark源码解析之三、任务切分与运行
关于源代码的前期准备可以参考:spark源码解析之一、整体概述

一、源码时序图

本次源码跟踪是在yarn-cluster模式下的原码,在源码中只关注cluster模式,如果没有特殊说明,默认yarn-cluster模式。
鉴于spark源码的复杂性,为了今后复习方便,按照时间先后顺序将spark应用启动的资源准备线绘制出时序图。在时序图中只是列举了重要的节点,与主题无关的类或者对象没有列举,在绘制图形是没有严格按照时序图的标准进行,有问题请各位即使批评指正,相互学习。
在这里插入图片描述

二、源码详细分析

源码上下文顺序与时序图顺序基本保持一致,一级标题例如标识类,这里没有展示全类名;二级标题标识该类下的方法,源码的上下文并没有按照标题顺序,而是按照逻辑调用的顺序进行,所以二级标题是错乱的,但是在一级标题下的二级标题是有序的。

1 SparkSubmit

spark_submit 提交任务,命令会调起脚本启动SparkSubmit进程,所以spark资源环境的准备的程序入口就从 SparkSubmit.main方法开始。

1.1 SparkSubmit.main
  def main(args: Array[String]): Unit = {
   
    //解析spark-submit命令提交的参数
    val appArgs = new SparkSubmitArguments(args)
    ....
    appArgs.action match {
   
      //我们只关注提交SUBMIT的情况,参考1.2
      case SparkSubmitAction.SUBMIT => submit(appArgs)
      case SparkSubmitAction.KILL => kill(appArgs)
      case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs)
    }
  }

通过解析任务脚本提交的参数,这里我们只关注提交submit这一种情况,所以接下来关注SparkSubmit.submit方法。

1.2 SparkSubmit.submit
  private def submit(args: SparkSubmitArguments): Unit = {
   
	  //解析提交环境参数,参考1.3
    val (childArgs, childClasspath, sysProps, childMainClass) = prepareSubmitEnvironment(args)
    //这里只是定义一个方法,不会自动执行,只有被调用才会执行
    def doRunMain(): Unit = {
   
      if (args.proxyUser != null) {
   
        val proxyUser = UserGroupInformation.createProxyUser(args.proxyUser,
          UserGroupInformation.getCurrentUser())
        try {
   
          proxyUser.doAs(new PrivilegedExceptionAction[Unit]() {
   
            override def run(): Unit = {
   
				//根据解析的环境资源参数调用子类方法
              runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose)
            }
          })
        } catch {
   
          ....
        }
      } else {
   
        //参考1.4
        runMain(childArgs, childClasspath, sysProps, childMainClass, args.verbose)
      }
    }

    //StandaloneCluster模式
    if (args.isStandaloneCluster && args.useRest) {
   
    ....
    //其它模式
    } else {
   
		//调用doRunMain方法
      doRunMain()
    }
  }

该方法主要分两步,预先准备任务提交的环境参数信息,然后将环境参数信息提交。首先关注SparkSubmit的prepareSubmitEnvironment方法,这里边的信息决定了我们接下来调用的方法。

1.3 SparkSubmit.prepareSubmitEnvironment

主要为了准备提交环境,这里主要关注childMainClass 。

  private[deploy] def prepareSubmitEnvironment(args: SparkSubmitArguments)
      : (Seq[String], Seq[String], Map[String, String], String) = {
   
     ....
    //isYarnCluster:yarn集群模式
    if (isYarnCluster) {
   
      //需要特别关注
      childMainClass = "org.apache.spark.deploy.yarn.Client"
      ....
    }
     ....
    (childArgs, childClasspath, sysProps, childMainClass)
  }

在yarn集群模式模式下反回了一个重要的参数childMainClass = “org.apache.spark.deploy.yarn.Client”,接下来关注一下SparkSubmit.runMain。

1.4 SparkSubmit.runMain

下一步参考 2.1 main

  private def runMain(
      childArgs: Seq[String],
      childClasspath: Seq[String],
      sysProps: Map[String, String],
      childMainClass: String,
      verbose: Boolean): Unit = {
   
    var mainClass: Class[_] = null

    try {
   
      //根据反射获取childMainClass的类信息。
      mainClass = Utils.classForName(childMainClass)
    } catch {
   
      ....
    }
    //获取childMainClass的main方法
    val mainMethod = mainClass.getMethod("main", new Array[String](0).getClass)
    if (!Modifier.isStatic(mainMethod.getModifiers)) {
   
      throw new IllegalStateException("The main method in the given main class must be static")
    }

    try {
   
     //反射调用org.apache.spark.deploy.yarn.Client的main方法
      mainMethod.invoke(null, childArgs.toArray)
    } catch {
   
     ....
    }
  }

该方法主要通过反射调用了org.apache.spark.deploy.yarn.Client的main方法。

2 Client

2.1 Client.main
  def main(argStrings: Array[String]) {
   
    ....
    //调用Client的run方法
    new Client(args, sparkConf).run()
  }

创建org.apache.spark.deploy.yarn.Client对象并调用run方法。

2.2 Client.run
  //向ResourceManager提交应用程序
  def run(): Unit = {
   
    this.appId = submitApplication()
    ....
  }

该方法主要是提交应用到ResourceManager。通过接下来主要关注一下Client.submitApplication。

2.3 Client.submitApplication
 //向ResourceManager提交一个运行ApplicationMaster的应用程序
  def submitApplication(): ApplicationId = {
   
    var appId: ApplicationId = null
    try {
   
      launcherBackend.connect()
      //在执行其他操作之前设置凭据
      setupCredentials()
      yarnClient.init(yarnConf)
	  //启动yarnClient,与yarn建立连接
      yarnClient.start()

      // 从AM创建一个应用
      val newApp = yarnClient.createApplication()
      val newAppResponse = newApp.getNewApplicationResponse()
      appId = newAppResponse.getApplicationId()
      ....
      new CallerContext("CLIENT", Option(appId.toString)).setCurrentContext()
      ....
      //设置适当的上下文来启动AM
      val containerContext = createContainerLaunchContext(newAppResponse)
      val appContext = createApplicationSubmissionContext(newApp, containerContext)
      ....
	  //向yarn的ResourceManager提交应用
	  //这里主要提交前边方法封装的命令,提交之后才会执行createContainerLaunchContext封装的逻辑和命令
      yarnClient.submitApplication(appContext)
      appId
    } catch {
   
      ....
    }
  }
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值