spark源码-任务提交流程之-1-sparkSubmit

26 篇文章 0 订阅
25 篇文章 2 订阅

1.spark-cluster任务提交流程图

在这里插入图片描述

2.sparkSubmit源码

2.1.main

启动sparkSubmit的JVM进程后,调用sparkSubmit伴生对象的main方法;

object SparkSubmit extends CommandLineUtils with Logging {
  override def main(args: Array[String]): Unit = {
  	//实例化sparkSubmit对象
    val submit = new SparkSubmit() {
      self =>

	  //重写参数解析方法
      override protected def parseArguments(args: Array[String]): SparkSubmitArguments = {
      	//封装spark提交参数
        new SparkSubmitArguments(args) {
          override protected def logInfo(msg: => String): Unit = self.logInfo(msg)

          override protected def logWarning(msg: => String): Unit = self.logWarning(msg)
        }
      }

      override protected def logInfo(msg: => String): Unit = printMessage(msg)

      override protected def logWarning(msg: => String): Unit = printMessage(s"Warning: $msg")

	  //重写doSubmit方法
      override def doSubmit(args: Array[String]): Unit = {
        try {
          //调用SparkSubmit的doSubmit方法
          super.doSubmit(args)
        } catch {
          case e: SparkUserAppException =>
            exitFn(e.exitCode)
        }
      }

    }
	//调用重写后的doSubmit
    submit.doSubmit(args)
  }
}

2.1.1.SparkSubmitArguments spark提交参数类

该类用于封装spark提交时的参数;
通过解析命令行、默认配置文件、env中的参数,使用以spark.开头的参数填充对象属性值;保证重要参数不为空;

private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, String] = sys.env)
  extends SparkSubmitArgumentsParser with Logging {
  //以下时支持封装的参数
  var master: String = null
  var deployMode: String = null
  var executorMemory: String = null
  var executorCores: String = null
  var totalExecutorCores: String = null
  var propertiesFile: String = null
  var driverMemory: String = null
  var driverExtraClassPath: String = null
  var driverExtraLibraryPath: String = null
  var driverExtraJavaOptions: String = null
  var queue: String = null
  var numExecutors: String = null
  var files: String = null
  var archives: String = null
  var mainClass: String = null
  var primaryResource: String = null
  var name: String = null
  var childArgs: ArrayBuffer[String] = new ArrayBuffer[String]()
  var jars: String = null
  var packages: String = null
  var repositories: String = null
  var ivyRepoPath: String = null
  var ivySettingsPath: Option[String] = None
  var packagesExclusions: String = null
  var verbose: Boolean = false
  var isPython: Boolean = false
  var pyFiles: String = null
  var isR: Boolean = false
  var action: SparkSubmitAction = null
  val sparkProperties: HashMap[String, String] = new HashMap[String, String]()
  var proxyUser: String = null
  var principal: String = null
  var keytab: String = null
  private var dynamicAllocationEnabled: Boolean = false

  // Standalone cluster mode only
  var supervise: Boolean = false
  var driverCores: String = null
  var submissionToKill: String = null
  var submissionToRequestStatusFor: String = null
  var useRest: Boolean = false // used internally

  //解析spark提交时的命令行参数
  parse(args.asJava)

  //解析spark 默认参数文件中的参数
  mergeDefaultSparkProperties()
  //从sparkProperties中移除不是以spark.开头的参数
  ignoreNonSparkProperties()
  //从sparkProperties和env中获取数据填充SparkSubmitArguments对象的属性值,没有的置空
  loadEnvironmentArguments()

  //重要参数校验
  validateArguments()
}

2.2.SparkSubmit.doSubmit方法

private[spark] class SparkSubmit extends Logging {
  def doSubmit(args: Array[String]): Unit = {
    //初始化日志记录;同时标记日志记录是否需要在应用程序启动之前重置
    val uninitLog = initializeLogIfNecessary(true, silent = true)

	//调用main方法中实例化sparkSubmit对象时重写的参数解析方法,解析并封装spark提交参数
    val appArgs = parseArguments(args)
    if (appArgs.verbose) {
      logInfo(appArgs.toString)
    }
    appArgs.action match {
      //提交逻辑
      case SparkSubmitAction.SUBMIT => submit(appArgs, uninitLog)
      case SparkSubmitAction.KILL => kill(appArgs)
      case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs)
      case SparkSubmitAction.PRINT_VERSION => printVersion()
    }
  }
}

2.3.SparkSubmit.submit

调用SparkSubmit.runMain方法;

private[spark] class SparkSubmit extends Logging {
  private def submit(args: SparkSubmitArguments, uninitLog: Boolean): Unit = {
	//定义包含针对代理者的runMain方法
    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(args, uninitLog)
            }
          })
        } catch {
          case e: Exception =>
            if (e.getStackTrace().length == 0) {
              error(s"ERROR: ${e.getClass().getName()}: ${e.getMessage()}")
            } else {
              throw e
            }
        }
      } else {
        //调用SparkSubmit.runMain方法;
        runMain(args, uninitLog)
      }
    }
    
    if (args.isStandaloneCluster && args.useRest) {
      try {
        logInfo("Running Spark using the REST application submission protocol.")
        doRunMain()
      } catch {
        case e: SubmitRestConnectionException =>
          logWarning(s"Master endpoint ${args.master} was not a REST server. " +
            "Falling back to legacy submission gateway instead.")
          args.useRest = false
          submit(args, false)
      }
    } else {
      doRunMain()
    }
  }
}

2.4.SparkSubmit.runMain

准备环境
根据main Class构造SparkApplication
启动SparkApplication

private[spark] class SparkSubmit extends Logging {
  private def runMain(args: SparkSubmitArguments, uninitLog: Boolean): Unit = {
  	//准备提交申请的环境
    val (childArgs, childClasspath, sparkConf, childMainClass) = prepareSubmitEnvironment(args)
    //让主类在启动日志系统后重新初始化日志系统
    if (uninitLog) {
      Logging.uninitialize()
    }

    if (args.verbose) {
      logInfo(s"Main class:\n$childMainClass")
      logInfo(s"Arguments:\n${childArgs.mkString("\n")}")
      // sysProps may contain sensitive information, so redact before printing
      logInfo(s"Spark config:\n${Utils.redact(sparkConf.getAll.toMap).mkString("\n")}")
      logInfo(s"Classpath elements:\n${childClasspath.mkString("\n")}")
      logInfo("\n")
    }

	//开启类加载器线程
    val loader =
      if (sparkConf.get(DRIVER_USER_CLASS_PATH_FIRST)) {
        new ChildFirstURLClassLoader(new Array[URL](0),
          Thread.currentThread.getContextClassLoader)
      } else {
        new MutableURLClassLoader(new Array[URL](0),
          Thread.currentThread.getContextClassLoader)
      }
    Thread.currentThread.setContextClassLoader(loader)

	//加载jar包
    for (jar <- childClasspath) {
      addJarToClasspath(jar, loader)
    }

    var mainClass: Class[_] = null

    try {
      //通过反射实例化mainClass类
      //yarn-cluster模式下,mainClss=org.apache.spark.deploy.yarn.YarnClusterApplication
      mainClass = Utils.classForName(childMainClass)
    } catch {
      case e: ClassNotFoundException =>
        logWarning(s"Failed to load $childMainClass.", e)
        if (childMainClass.contains("thriftserver")) {
          logInfo(s"Failed to load main class $childMainClass.")
          logInfo("You need to build Spark with -Phive and -Phive-thriftserver.")
        }
        throw new SparkUserAppException(CLASS_NOT_FOUND_EXIT_STATUS)
      case e: NoClassDefFoundError =>
        logWarning(s"Failed to load $childMainClass: ${e.getMessage()}")
        if (e.getMessage.contains("org/apache/hadoop/hive")) {
          logInfo(s"Failed to load hive class.")
          logInfo("You need to build Spark with -Phive and -Phive-thriftserver.")
        }
        throw new SparkUserAppException(CLASS_NOT_FOUND_EXIT_STATUS)
    }
	
	//根据mainClass类实例化application
    val app: SparkApplication = if (classOf[SparkApplication].isAssignableFrom(mainClass)) {
      mainClass.newInstance().asInstanceOf[SparkApplication]
    } else {
      // SPARK-4170
      if (classOf[scala.App].isAssignableFrom(mainClass)) {
        logWarning("Subclasses of scala.App may not work correctly. Use a main() method instead.")
      }
      new JavaMainApplication(mainClass)
    }

    @tailrec
    def findCause(t: Throwable): Throwable = t match {
      case e: UndeclaredThrowableException =>
        if (e.getCause() != null) findCause(e.getCause()) else e
      case e: InvocationTargetException =>
        if (e.getCause() != null) findCause(e.getCause()) else e
      case e: Throwable =>
        e
    }

    try {
      //调用application的start方法
      //yarn-cluster模式下,调用org.apache.spark.deploy.yarn.YarnClusterApplication类的start方法
      app.start(childArgs.toArray, sparkConf)
    } catch {
      case t: Throwable =>
        throw findCause(t)
    }
  }
}

2.4.1.mainClass的确定

sparkSubmit.runMain()—>SparkSubmit.prepareSubmitEnvironment()—>SparkSubmit.YARN_CLUSTER_SUBMIT_CLASS

private[spark] class SparkSubmit extends Logging {
  private def runMain(args: SparkSubmitArguments, uninitLog: Boolean): Unit = {
    val (childArgs, childClasspath, sparkConf, childMainClass) = prepareSubmitEnvironment(args)
  }

  private[deploy] def prepareSubmitEnvironment(
      args: SparkSubmitArguments,
      conf: Option[HadoopConfiguration] = None)
      : (Seq[String], Seq[String], SparkConf, String) = {
  	if (isYarnCluster) {
      childMainClass = YARN_CLUSTER_SUBMIT_CLASS
    }
  }
}

object SparkSubmit extends CommandLineUtils with Logging {
  private[deploy] val YARN_CLUSTER_SUBMIT_CLASS =
    "org.apache.spark.deploy.yarn.YarnClusterApplication"
}

3.sparkSubmit总结

解析命令行、默认配置文件、env中参数;
准备提交application的环境;
获取main Class,反射构造SparkApplication,启动SparkApplication;

4.参考资料

spark源码之任务提交流程
Spark源码系列(一):Spark作业提交流程
spark提交流程-源码分析
Spark内核之YARN Cluster模式源码详解(Submit详解)
Spark源码解析之——YARN Cluster模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark submit任务提交是指将用户编写的Spark应用程序提交到集群中运行的过程。在Spark中,用户可以通过命令行工具或API方式提交任务Spark submit命令的基本语法如下: ``` ./bin/spark-submit \ --class <main-class> \ --master <master-url> \ --deploy-mode <deploy-mode> \ --conf <key>=<value> \ <application-jar> \ [application-arguments] ``` 其中,`--class`指定应用程序的主类,`--master`指定集群的URL,`--deploy-mode`指定应用程序的部署模式,`--conf`指定应用程序的配置参数,`<application-jar>`指定应用程序的jar包路径,`[application-arguments]`指定应用程序的命令行参数。 在Spark中,任务提交的过程主要包括以下几个步骤: 1. 创建SparkConf对象,设置应用程序的配置参数; 2. 创建SparkContext对象,连接到集群; 3. 加载应用程序的主类; 4. 运行应用程序的main方法; 5. 关闭SparkContext对象,释放资。 在任务提交的过程中,Spark会自动将应用程序的jar包和依赖的库文件上传到集群中,并在集群中启动Executor进程来执行任务任务执行完成后,Spark会将结果返回给Driver进程,并将Executor进程关闭。 总之,Spark submit任务提交Spark应用程序运行的关键步骤,掌握任务提交的原理和方法对于开发和调试Spark应用程序非常重要。 ### 回答2: Spark 作为一款强大的分布式计算框架,提供了很多提交任务的方式,其中最常用的方法就是通过 spark-submit 命令来提交任务spark-submit 是 Spark 提供的一个命令行工具,用于在集群上提交 Spark 应用程序,并在集群上运行。 spark-submit 命令的语法如下: ``` ./bin/spark-submit [options] <app jar | python file> [app arguments] ``` 其中,[options] 为可选的参数,包括了执行模式、执行资等等,<app jar | python file> 为提交的应用程序的文件路径,[app arguments] 为应用程序运行时的参数。 spark-submit 命令会将应用程序的 jar 文件以及所有的依赖打包成一个 zip 文件,然后将 zip 文件提交到集群上运行。在运行时,Spark 会根据指定的主类(或者 Python 脚本文件)启动应用程序。 在提交任务时,可以通过设置一些参数来控制提交任务的方式。例如: ``` --master:指定该任务运行的模式,默认为 local 模式,可设置为 Spark Standalone、YARN、Mesos、Kubernetes 等模式。 --deploy-mode:指定该任务的部署模式,默认为 client,表示该应用程序会在提交任务的机器上运行,可设置为 cluster,表示该应用程序会在集群中一台节点上运行。 --num-executors:指定该任务需要的 executor 数量,每个 executor 会占用一个计算节点,因此需要根据集群配置与任务要求确定该参数的值。 --executor-memory:指定每个 executor 可用的内存量,默认为 1g,可以适当调整该值以达到更好的任务运行效果。 ``` 此外,还有一些参数可以用来指定应用程序运行时需要传递的参数: ``` --conf:指定应用程序运行时需要的一些配置参数,比如 input 文件路径等。 --class:指定要运行的类名或 Python 脚本文件名。 --jars:指定需要使用的 Jar 包文件路径。 --py-files:指定要打包的 python 脚本,通常用于将依赖的 python 包打包成 zip 文件上传。 ``` 总之,spark-submit 是 Spark 提交任务最常用的方法之一,通过该命令能够方便地将应用程序提交到集群上运行。在提交任务时,需要根据实际场景调整一些参数,以达到更好的任务运行效果。 ### 回答3: Spark是一个高效的分布式计算框架,其中比较重要的组成部分就是任务提交。在Spark中,任务提交主要通过spark-submit来实现。本文将从两方面,即任务提交之前的准备工作和任务提交过程中的细节进行探讨。 一、任务提交之前的准备工作 1.环境配置 在执行任务提交前,需要确保所在的计算机环境已经配置好了SparkSpark的环境配置主要包括JAVA环境、Spark的二进制包、PATH路径配置、SPARK_HOME环境变量配置等。 2.编写代码 Spark任务提交是基于代码的,因此在任务提交前,需要编写好自己的代码,并上传到集群中的某个路径下,以便后续提交任务时调用。 3.参数设置 在任务提交时,需要对一些关键的参数进行设置。例如,任务名、任务对应的代码路径、任务需要的资任务需要的worker节点等。 二、任务提交过程中的细节 1.启动Driver 当使用spark-submit命令提交任务时,Spark会启动一个Driver来运行用户的代码。这个Driver通常需要连接到Spark集群来执行任务。 2.上传文件 Spark支持在任务提交时上传所需的文件。这些文件可以用于设置Spark的环境变量、为任务提供数据等。 3.资需求 Spark任务执行依赖于一定的资。每个任务可以指定自己的资需求,例如需要多少内存、需要多少CPU等。这些资需求通常与提交任务时需要的worker节点数量有关系。 4.监控和日志 在任务执行的过程中,Spark会收集任务的监控数据和日志信息。这些数据可用于后续的调试和性能优化。 总之,在Spark任务提交过程中,需要充分考虑任务的资需求和监控日志信息的收集,以便更好地完成任务和优化Spark运行效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值