转自:https://github.com/keepsimplefocus/spark-sourcecodes-analysis/blob/master/markdowns/Spark%E8%AF%BB%E5%8F%96%E9%85%8D%E7%BD%AE.md

Spark读取配置

我们知道,有一些配置可以在多个地方配置。以配置executor的memory为例,有以下三种方式: 1. spark-submit的--executor-memory选项 2. spark-defaults.conf的spark.executor.memory配置 3. spark-env.sh的SPARK_EXECUTOR_MEMORY配置

同一个配置可以在多处设置,这显然会造成迷惑,不知道spark为什么到现在还保留这样的逻辑。 如果我分别在这三处对executor的memory设置了不同的值,最终在Application中生效的是哪个?

处理这一问题的类是SparkSubmitArguments。在其构造函数中就完成了从 『spark-submit --选项』、『spark-defaults.conf』、『spark-env.sh』中读取配置,并根据策略决定使用哪个配置。下面分几步来分析这个重要的构造函数。

Step0:读取spark-env.sh配置并写入环境变量中

SparkSubmitArguments的参数列表包含一个env: Map[String, String] = sys.env参数。该参数包含一些系统环境变量的值和从spark-env.sh中读取的配置值,如图是我一个demo中env值的部分截图

这一步之所以叫做Step0,是因为env的值在构造SparkSubmitArguments对象之前就确认,即spark-env.sh在构造SparkSubmitArguments对象前就读取并将配置存入env中。

Step1:创建各配置成员并赋空值

这一步比较简单,定义了所有要从『spark-submit --选项』、『spark-defaults.conf』、『spark-env.sh』中读取的配置,并赋空值。下面的代码展示了其中一部分 :

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 packagesExclusions: String = null
var verbose: Boolean = false ...

Step2:调用父类parse方法解析 spark-submit --选项

  try {
parse(args.toList)
} catch {
case e: IllegalArgumentException => SparkSubmit.printErrorAndExit(e.getMessage())
}

这里调用父类的SparkSubmitOptionParser#parse(List<String> args)。parse函数查找args中设置的--选项和值并解析为name和value,如--master yarn-client会被解析为值为--master的name和值为yarn-client的value。这之后调用SparkSubmitArguments#handle(MASTER, "yarn-client")进行处理。

来看看handle函数干了什么:


/** Fill in values by parsing user options. */
override protected def handle(opt: String, value: String): Boolean = {
opt match {
case NAME =>
name = value case MASTER =>
master = value case CLASS =>
mainClass = value case DEPLOY_MODE =>
if (value != "client" && value != "cluster") {
SparkSubmit.printErrorAndExit("--deploy-mode must be either \"client\" or \"cluster\"")
}
deployMode = value case NUM_EXECUTORS =>
numExecutors = value case TOTAL_EXECUTOR_CORES =>
totalExecutorCores = value case EXECUTOR_CORES =>
executorCores = value case EXECUTOR_MEMORY =>
executorMemory = value case DRIVER_MEMORY =>
driverMemory = value case DRIVER_CORES =>
driverCores = value case DRIVER_CLASS_PATH =>
driverExtraClassPath = value ... case _ =>
throw new IllegalArgumentException(s"Unexpected argument '$opt'.")
}
true
}

这个函数也很简单,根据参数opt及value,设置各个成员的值。接上例,parse中调用handle("--master", "yarn-client")后,在handle函数中,master成员将被赋值为yarn-client

注意,case MASTER中的MASTER的值在SparkSubmitOptionParser定义为--master,MASTER与其他值定义如下:

protected final String MASTER = "--master";

protected final String CLASS = "--class";
protected final String CONF = "--conf";
protected final String DEPLOY_MODE = "--deploy-mode";
protected final String DRIVER_CLASS_PATH = "--driver-class-path";
protected final String DRIVER_CORES = "--driver-cores";
protected final String DRIVER_JAVA_OPTIONS = "--driver-java-options";
protected final String DRIVER_LIBRARY_PATH = "--driver-library-path";
protected final String DRIVER_MEMORY = "--driver-memory";
protected final String EXECUTOR_MEMORY = "--executor-memory";
protected final String FILES = "--files";
protected final String JARS = "--jars";
protected final String KILL_SUBMISSION = "--kill";
protected final String NAME = "--name";
protected final String PACKAGES = "--packages";
protected final String PACKAGES_EXCLUDE = "--exclude-packages";
protected final String PROPERTIES_FILE = "--properties-file";
protected final String PROXY_USER = "--proxy-user";
protected final String PY_FILES = "--py-files";
protected final String REPOSITORIES = "--repositories";
protected final String STATUS = "--status";
protected final String TOTAL_EXECUTOR_CORES = "--total-executor-cores"; ...

总结来说,parse函数解析了spark-submit中的--选项,并根据解析出的name和value给SparkSubmitArguments的各个成员(例如master、deployMode、executorMemory等)设置值。

Step3:mergeDefaultSparkProperties加载spark-defaults.conf中配置

Step3读取spark-defaults.conf中的配置文件并存入sparkProperties中,sparkProperties将在下一步中发挥作用

//< 保存从spark-defaults.conf读取的配置
val sparkProperties: HashMap[String, String] = new HashMap[String, String]() //< 获取配置文件路径,若在spark-env.sh中设置SPARK_CONF_DIR,则以该值为准;否则为 $SPARK_HOME/conf/spark-defaults.conf
def getDefaultPropertiesFile(env: Map[String, String] = sys.env): String = {
env.get("SPARK_CONF_DIR")
.orElse(env.get("SPARK_HOME").map { t => s"$t${File.separator}conf" })
.map { t => new File(s"$t${File.separator}spark-defaults.conf")}
.filter(_.isFile)
.map(_.getAbsolutePath)
.orNull
} //< 读取spark-defaults.conf配置并存入sparkProperties中
private def mergeDefaultSparkProperties(): Unit = {
// Use common defaults file, if not specified by user
propertiesFile = Option(propertiesFile).getOrElse(Utils.getDefaultPropertiesFile(env))
// Honor --conf before the defaults file
defaultSparkProperties.foreach { case (k, v) =>
if (!sparkProperties.contains(k)) {
sparkProperties(k) = v
}
}
}

Step4:loadEnvironmentArguments确认每个配置成员最终值

先来看看代码(由于篇幅太长,省略了一部分)

  private def loadEnvironmentArguments(): Unit = {
master = Option(master)
.orElse(sparkProperties.get("spark.master"))
.orElse(env.get("MASTER"))
.orNull
driverExtraClassPath = Option(driverExtraClassPath)
.orElse(sparkProperties.get("spark.driver.extraClassPath"))
.orNull
driverExtraJavaOptions = Option(driverExtraJavaOptions)
.orElse(sparkProperties.get("spark.driver.extraJavaOptions"))
.orNull
driverExtraLibraryPath = Option(driverExtraLibraryPath)
.orElse(sparkProperties.get("spark.driver.extraLibraryPath"))
.orNull
driverMemory = Option(driverMemory)
.orElse(sparkProperties.get("spark.driver.memory"))
.orElse(env.get("SPARK_DRIVER_MEMORY"))
.orNull ... keytab = Option(keytab).orElse(sparkProperties.get("spark.yarn.keytab")).orNull
principal = Option(principal).orElse(sparkProperties.get("spark.yarn.principal")).orNull // Try to set main class from JAR if no --class argument is given
if (mainClass == null && !isPython && !isR && primaryResource != null) {
val uri = new URI(primaryResource)
val uriScheme = uri.getScheme() uriScheme match {
case "file" =>
try {
val jar = new JarFile(uri.getPath)
// Note that this might still return null if no main-class is set; we catch that later
mainClass = jar.getManifest.getMainAttributes.getValue("Main-Class")
} catch {
case e: Exception =>
SparkSubmit.printErrorAndExit(s"Cannot load main class from JAR $primaryResource")
}
case _ =>
SparkSubmit.printErrorAndExit(
s"Cannot load main class from JAR $primaryResource with URI $uriScheme. " +
"Please specify a class through --class.")
}
} // Global defaults. These should be keep to minimum to avoid confusing behavior.
master = Option(master).getOrElse("local[*]") // In YARN mode, app name can be set via SPARK_YARN_APP_NAME (see SPARK-5222)
if (master.startsWith("yarn")) {
name = Option(name).orElse(env.get("SPARK_YARN_APP_NAME")).orNull
} // Set name from main class if not given
name = Option(name).orElse(Option(mainClass)).orNull
if (name == null && primaryResource != null) {
name = Utils.stripDirectory(primaryResource)
} // Action should be SUBMIT unless otherwise specified
action = Option(action).getOrElse(SUBMIT)
}

我们单独以确定master值的那部分代码来说明,相关代码如下

master = Option(master)
.orElse(sparkProperties.get("spark.master"))
.orElse(env.get("MASTER"))
.orNull // Global defaults. These should be keep to minimum to avoid confusing behavior.
master = Option(master).getOrElse("local[*]")

确定master的值的步骤如下: 1. Option(master):若master值不为null,则以master为准;否则进入2。若master不为空,从上文的分析我们可以知道是从解析spark-submit --master选项得到的值 2..orElse(sparkProperties.get("spark.master")):若sparkProperties.get("spark.master")范围非null则以该返回值为准;否则进入3。从Step3中可以知道sparkProperties中的值都是从spark-defaults.conf中读取 3..orElse(env.get("MASTER")):若env.get("MASTER")返回非null,则以该返回值为准;否则进入4。env中的值从spark-env.sh读取而来 4. 若以上三处均为设置master,则取默认值local[*]

查看其余配置成员的值的决定过程也和master一致,稍有不同的是并不是所有配置都能在spark-defaults.conf、spark-env.sh和spark-submit选项中设置。但优先级还是一致的。

由此,我们可以得出结论,对于spark配置。若一个配置在多处设置,则优先级如下: spark-submit --选项 > spark-defaults.conf配置 > spark-env.sh配置 > 默认值

最后,附上流程图

Spark读取配置(转)的更多相关文章

  1. Spark参数配置

    转自:http://hadoop1989.com/2015/10/08/Spark-Configuration/ 一.Spark参数设置 二.查看Spark参数设置 三.Spark参数分类 四.Spa ...

  2. spark读取外部配置文件的方法

    spark读取外部配置文件的方法 spark-submit  --files /tmp/fileName /tmp/test.jar 使用spark提交时使用--files参数,spark会将将本地的 ...

  3. Spark 属性配置

    1.Spark1.x 属性配置方式 Spark属性提供了大部分应用程序的控制项,并且可以单独为每个应用程序进行配置. 在Spark1.0.0提供了3种方式的属性配置: SparkConf方式 Spar ...

  4. .NET Core采用的全新配置系统[1]: 读取配置数据

    提到“配置”二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化的配置定义在这两个文 ...

  5. ASP.NET Core的配置(1):读取配置信息

    提到"配置"二字,我想绝大部分.NET开发人员脑海中会立马浮现出两个特殊文件的身影,那就是我们再熟悉不过的app.config和web.config,多年以来我们已经习惯了将结构化 ...

  6. 解决Spark读取Hive分区表出现Input path does not exist的问题

    假设这里出错的表为test表. 现象 Hive读取正常,不会报错,Spark读取就会出现: org.apache.hadoop.mapred.InvalidInputException: Input ...

  7. SQL2005SP4补丁安装时错误: -2146233087 MSDTC 无法读取配置信息。。。错误代码1603的解决办法

    是在安装slq2005sp3和sp4补丁的时候碰到的问题. 起先是碰到的错误1603的问题,但网上搜索的1603的解决办法都试过了,google也用了,外文论坛也读了,依然没有能解决这个问题. 其实一 ...

  8. JavaWEB中读取配置信息

    第一种方法是使用java.io和java.util包,缺点是路径的概念要清晰, 例子: Properties prop = new Properties(); InputStream in = get ...

  9. 无法读取配置节“protocolMapping”,因为它缺少节声明

    无法读取配置节“protocolMapping”,因为它缺少节声明 1.正常情况   :  Web.config文件中有protocolMapping节点, 发现在IIS部署时使用了.NET 2.0的 ...

随机推荐

  1. Windows和pthread中提供的自旋锁

    Windows和POSIX中都提供了自旋锁,我们也可以通过C++11的atomic来实现自旋锁.那么两者性能上面是什么关系?先引入实现代码: #ifndef __spinlock_h__ #defin ...

  2. python打包--pyinstaller打包报错

    先来一段报错信息  目前打包主要涉及socket模块出现的报错 missing module named resource - imported by posix, C:\Users\Administ ...

  3. window.open打开新窗口报错ie 位指明错误,原因是window没有加引号!

    function JsMod(htmlurl,tmpWidth,tmpHeight){ htmlurl=getRandomUrl(htmlurl); var newwin = window.open( ...

  4. C#深入解析委托——C#中为什么要引入委托

    引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以 ...

  5. 转载-----BUFG,IBUFG,BUFGP,IBUFGDS等含义以及使用

    目前,大型设计一般推荐使用同步时序电路.同步时序电路基于时钟触发沿设计,对时钟的周期.占空比.延时和抖动提出了更高的要求.为了满足同步时序设计的要求,一般在FPGA设计中采用全局时钟资源驱动设计的主时 ...

  6. Hive调优

    Hive存储格式选择 和Hive 相关优化: 压缩参考 Hive支持的存储数的格式主要有:TEXTFILE .SEQUENCEFILE.ORC.PARQUET. 文件存储格式 列式存储和行式存储 行存 ...

  7. Java - 31 Java 发送邮件

    Java 发送邮件 使用Java应用程序发送E-mail十分简单,但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) . 你可以在 ...

  8. 学习docker后的个人理解

    一.什么是docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源.可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行 ...

  9. KVM总结-KVM性能优化之网络性能优化

    前面已经介绍了KVM CPU优化(http://blog.csdn.net/dylloveyou/article/details/71169463).内存优化(http://blog.csdn.net ...

  10. RecyclerView.Adapter封装,最简单实用的BaseRecyclerViewAdapter;只需重写一个方法,设置数据链式调用;

    之前对ListView的BaseAdapter进行过封装,只需重写一个getView方法: 现在慢慢的RecyclerView成为主流,下面是RecyclerView.Adapter的封装: Base ...