Spark 源码 | SparkSubmitArguments 参数解析(三)

作者:董可伦日期:2026/7/17

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:https://www.captainai.net/dongkelun

前言

在第二篇文章 Spark 源码 | SparkSubmit 提交流程分析(二) 中,我们分析了 SparkSubmit 的提交流程,其中 doSubmit 方法会调用 parseArguments 解析命令行参数。本文详细介绍参数解析的过程。

版本

Spark 3.2.3

SparkSubmitArguments 概述

SparkSubmitArguments 继承自 SparkSubmitArgumentsParser,用于解析 spark-submit 命令行参数。

1// SparkSubmitArguments.scala
2private[deploy] class SparkSubmitArguments(args: Seq[String], env: Map[String, String] = sys.env)
3  extends SparkSubmitArgumentsParser with Logging {
4

字段定义

1// --master MASTER_URL
2// Spark Master 地址,如 spark://host:port, mesos://host:port, yarn, k8s://https://host:port, local(默认 local[*])
3var master: String = null
4
5// --deploy-mode DEPLOY_MODE
6// 部署模式,client 表示在本地启动 Driver,cluster 表示在集群的 Worker 节点上启动 Driver(默认 client)
7var deployMode: String = null
8
9// --executor-memory MEM
10// 每个 Executor 的内存(如 1000M, 2G)(默认 1G)
11var executorMemory: String = null
12
13// --executor-cores NUM
14// 每个 Executor  CPU 核心数(YARN  K8S 模式默认 1,Standalone 模式默认为 Worker 上的所有核心)
15var executorCores: String = null
16
17// --total-executor-cores NUM
18// 所有 Executor 的总 CPU 核心数
19var totalExecutorCores: String = null
20
21// --properties-file FILE
22// 配置文件路径,用于加载额外的 Spark 配置。如果未指定,则默认查找 conf/spark-defaults.conf
23var propertiesFile: String = null
24
25// --driver-memory MEM
26// Driver 的内存(如 1000M, 2G)
27var driverMemory: String = null
28
29// --driver-class-path
30// 额外添加到 Driver classpath 的路径。注意:通过 --jars 添加的 jar 包会自动包含在 classpath 
31var driverExtraClassPath: String = null
32
33// --driver-library-path
34// 额外添加到 Driver 库路径的环境变量
35var driverExtraLibraryPath: String = null
36
37// --driver-java-options
38// 传递给 Driver 的额外 Java 选项
39var driverExtraJavaOptions: String = null
40
41// --queue QUEUE_NAME
42// YARN 队列名称(默认 "default")
43var queue: String = null
44
45// --num-executors NUM
46// Executor 的数量(默认 2)。启用动态分配时,实际初始数量取 --num-executors、
47// spark.dynamicAllocation.minExecutors  spark.dynamicAllocation.initialExecutors 三者中的最大值
48var numExecutors: String = null
49
50// --files FILES
51// 逗号分隔的文件列表,会被放置在每个 Executor 的工作目录中。在 Executor 中可通过 SparkFiles.get(fileName) 获取文件路径
52var files: String = null
53
54// --archives ARCHIVES
55// 逗号分隔的归档文件列表,会被提取到每个 Executor 的工作目录中
56var archives: String = null
57
58// --class CLASS_NAME
59// 应用程序的主类(用于 Java / Scala 应用)
60var mainClass: String = null
61
62// <app jar | python file | R file>
63// 第一个未被识别的选项被视为"主资源"(primary resource)
64var primaryResource: String = null
65
66// --name NAME
67// 应用程序的名称
68var name: String = null
69
70// 应用程序参数
71var childArgs: ArrayBuffer[String] = new ArrayBuffer[String]()
72
73// --jars JARS
74// 逗号分隔的 JAR 包列表,会被添加到 Driver  Executor  classpath 
75var jars: String = null
76
77// --packages
78// 逗号分隔的 Maven 坐标列表,用于添加 JAR 包到 Driver  Executor  classpath。
79// 会先搜索本地 Maven 仓库,然后是 Maven 中央仓库,以及 --repositories 指定的远程仓库。
80// 坐标格式为 groupId:artifactId:version
81var packages: String = null
82
83// --repositories
84// 逗号分隔的远程仓库地址,用于搜索 --packages 指定的 Maven 坐标
85var repositories: String = null
86
87var ivyRepoPath: String = null
88
89var ivySettingsPath: Option[String] = None
90
91// --exclude-packages
92// 逗号分隔的 groupId:artifactId 列表,在解析 --packages 提供的依赖时排除这些包,以避免依赖冲突
93var packagesExclusions: String = null
94
95// --verbose, -v
96// 打印额外的调试输出
97var verbose: Boolean = false
98
99var isPython: Boolean = false
100
101// --py-files PY_FILES
102// 逗号分隔的 .zip、.egg  .py 文件列表,会被添加到 Python 应用的 PYTHONPATH 
103var pyFiles: String = null
104
105var isR: Boolean = false
106
107var action: SparkSubmitAction = null
108
109// 通过 `--conf` 和配置文件加载的 Spark 配置属性
110val sparkProperties: HashMap[String, String] = new HashMap[String, String]()
111
112// --proxy-user NAME
113// 提交应用程序时模拟的用户名。不能与 --principal / --keytab 同时使用
114var proxyUser: String = null
115
116// --principal PRINCIPAL
117// 用于登录 KDC  Principal(仅 YARN/K8s 模式)
118var principal: String = null
119
120// --keytab KEYTAB
121// 包含上述 Principal 对应 keytab 的文件路径
122var keytab: String = null
123
124private var dynamicAllocationEnabled: Boolean = false
125
126// --supervise
127// 如果设置,Driver 失败时会自动重启(仅 Spark Standalone  Mesos  cluster 部署模式)
128var supervise: Boolean = false
129
130// --driver-cores NUM
131// Driver 使用的 CPU 核心数,仅在 cluster 模式可用(默认 1)
132var driverCores: String = null
133
134// --kill SUBMISSION_ID
135// 如果设置,杀死指定的 Driver(Spark Standalone、Mesos  K8s  cluster 部署模式)
136var submissionToKill: String = null
137
138// --status SUBMISSION_ID
139// 如果设置,查询指定 Driver 的状态
140var submissionToRequestStatusFor: String = null
141
142// 内部使用,用于 REST API
143var useRest: Boolean = false
144

解析流程

1. 构造函数调用顺序

1// Set parameters from command line arguments
2// 从命令行参数设置参数
3parse(args.asJava)
4
5// Populate `sparkProperties` map from properties file
6// 从属性文件填充 sparkProperties
7mergeDefaultSparkProperties()
8// Remove keys that don't start with "spark." from `sparkProperties`.
9//  sparkProperties 中移除不以 "spark." 开头的键
10ignoreNonSparkProperties()
11// Use `sparkProperties` map along with env vars to fill in any missing parameters
12// 使用 sparkProperties 和环境变量填充缺失的参数
13loadEnvironmentArguments()
14
15useRest = sparkProperties.getOrElse("spark.master.rest.enabled", "false").toBoolean
16
17validateArguments()
18

解析顺序:

  1. parse(args.asJava) - 从命令行参数设置参数
  2. mergeDefaultSparkProperties() - 从属性文件填充 sparkProperties
  3. ignoreNonSparkProperties() - 从 sparkProperties 中移除不以 “spark.” 开头的键
  4. loadEnvironmentArguments() - 使用 sparkProperties 和环境变量填充缺失的参数
  5. validateArguments() - 验证参数

2. 解析命令行参数

继承自 SparkSubmitArgumentsParser(继承自 SparkSubmitOptionParser),重写 handlehandleUnknownhandleExtraArgs 三个回调方法处理参数。

SparkSubmitOptionParser parse 方法解析逻辑

parse(List<String> args) 方法的遍历逻辑:

1// 遍历所有参数
2for (idx = 0; idx < args.size(); idx++) {
3  // 支持 --master=spark://xxx 格式(等号分隔)
4  // 检查是否是 --xxx=value 格式,若是则拆分
5  Matcher m = eqSeparatedOpt.matcher(arg);
6  if (m.matches()) {
7    arg = m.group(1);
8    value = m.group(2);
9  }
10
11  // Look for options with a value.
12  // 第一步:在 opts 数组中查找(带参数的选项,如 --master, --class 等)
13  String name = findCliOption(arg, opts);
14  if (name != null) {
15    // If the option requires a value, fetch it.
16    // 命令行格式:--选项 值(分开的两参数),如 --master yarn
17    if (value == null) {
18      if (idx == args.size() - 1) {
19        throw new IllegalArgumentException(
20            String.format("Missing argument for option '%s'.", arg));
21      }
22      idx++;
23      value = args.get(idx);
24    }
25    if (!handle(name, value)) {
26      // handle 返回 false 的情况:--version(--help 会直接退出,不会返回)
27      break;
28    }
29    continue;
30  }
31
32  // Look for a switch.
33  // 第二步:在 switches 数组中查找(不带参数的选项,如 --help, --verbose 等)
34  name = findCliOption(arg, switches);
35  if (name != null) {
36    // handle 返回 false 的情况:--version(打印版本后退出)
37    if (!handle(name, null)) {
38      break;
39    }
40    continue;
41  }
42
43  // 第三步:既不在 opts 也不在 switches 中,调用 handleUnknown 处理未知选项
44  // SparkSubmitArguments 中将第一个未知选项作为 primaryResource,handleUnknown 始终返回 false
45  // 返回 false  break 退出循环,剩余参数作为应用参数
46  if (!handleUnknown(arg)) {
47    break;
48  }
49}
50
51// 跳过第一个未知选项(primaryResource),只把剩余参数传给 handleExtraArgs
52// 如果正常遍历完(无 break),idx == args.size(),不需要 idx++
53// 如果因 break 提前退出,idx < args.size(),需要跳过 primaryResource
54if (idx < args.size()) {
55  idx++;
56}
57
58// 第四步:解析完成后,剩余的参数作为应用参数调用 handleExtraArgs
59// 注意:循环可能因 break 提前退出,导致 idx < args.size()
60// 如:handleUnknown 返回 false 时(SparkSubmitArguments 将第一个未知选项作为 primaryResource)
61// spark-submit 命令格式:spark-submit [options] <app jar | python file> [app arguments]
62// 剩余的参数即 [app arguments],如 arg1 arg2 arg3
63handleExtraArgs(args.subList(idx, args.size()));
64
SparkSubmitArguments 重写的方法
1. handle 方法

处理已知的带参数选项,根据 opt(选项名)将 value 赋值给对应的字段。其中 CONF case 会直接将 --conf 的值存入 sparkProperties

1override protected def handle(opt: String, value: String): Boolean = {
2  opt match {
3    case NAME =>
4      name = value
5    case MASTER =>
6      master = value
7    case CLASS =>
8      mainClass = value
9    case DEPLOY_MODE =>
10      if (value != "client" && value != "cluster") {
11        error("--deploy-mode must be either \"client\" or \"cluster\"")
12      }
13      deployMode = value
14    case NUM_EXECUTORS =>
15      numExecutors = value
16    // ... 其他参数
17    // `--conf` KEY=VALUE 格式的配置项,直接存入 sparkProperties
18    // 由于是直接赋值(覆盖写),后续合并配置文件时,`--conf` 的值会被保留
19    case CONF =>
20      val (confName, confValue) = SparkSubmitUtils.parseSparkConfProperty(value)
21      sparkProperties(confName) = confValue
22    // ... 其他参数
23  }
24  // --version 时返回 false,其他情况返回 true
25  action != SparkSubmitAction.PRINT_VERSION
26}
27
2. handleUnknown 方法

处理未知选项,将第一个未知选项作为 primaryResource(主资源),如 myapp.jar、spark-shell、hdfs:///path/to/app.jar:

1override protected def handleUnknown(opt: String): Boolean = {
2  // 如果选项以 "-" 开头但不在已知选项中,报错
3  if (opt.startsWith("-")) {
4    error(s"Unrecognized option '$opt'.")
5  }
6
7  // 设置 primaryResource(主资源),如 myapp.jar、spark-shell、hdfs:///path/to/app.jar
8  // 如果不是 shell(spark-shell、pyspark-shell、sparkr-shell)或内部选项(spark-internal),则通过 Utils.resolveURI 解析为标准 URI 格式
9  // 如:./app.jar -> file:/path/to/app.jar,/path/app.jar -> file:/path/app.jar,hdfs://xxx -> hdfs://xxx
10  // 否则直接作为字符串(如 spark-shell 用于交互式界面,spark-internal 表示没有指定应用程序资源,用于交互式工具如 spark-sql 或测试场景)
11  primaryResource =
12    if (!SparkSubmit.isShell(opt) && !SparkSubmit.isInternal(opt)) {
13      Utils.resolveURI(opt).toString
14    } else {
15      opt
16    }
17
18  // 判断是否为 Python  R 应用
19  isPython = SparkSubmit.isPython(opt)
20  isR = SparkSubmit.isR(opt)
21
22  // 始终返回 false,退出循环
23  false
24}
25
3. handleExtraArgs 方法

将解析剩余的应用参数添加到 childArgs:

1override protected def handleExtraArgs(extra: JList[String]): Unit = {
2  //  SparkSubmitArguments 类中只有这一处赋值,没看到有其它地方赋值,感觉两种写法效果可能是相同的
3  // 使用 ++= 追加而非 = 赋值,可能为了和 SparkSubmit.scala 中对 childArgs 的操作风格保持一致
4  // childArgs  ArrayBuffer[String],extra.asScala  Seq[String]
5  // 使用 ++= 避免了显式类型转换(如 childArgs = extra.asScala.to[ArrayBuffer])
6  childArgs ++= extra.asScala
7}
8

3. 合并默认属性文件

将配置文件(默认 conf/spark-defaults.conf)中的属性合并到 sparkProperties 中。如果用户通过 --conf 指定了相同的配置项,则保留 --conf 的值(优先级更高)。

1private def mergeDefaultSparkProperties(): Unit = {
2  // Use common defaults file, if not specified by user
3  // 如果用户未指定,则使用默认属性文件
4  propertiesFile = Option(propertiesFile).getOrElse(Utils.getDefaultPropertiesFile(env))
5  // Honor `--conf` before the defaults file
6  // `--conf` 的优先级高于默认属性文件
7  defaultSparkProperties.foreach { case (k, v) =>
8    if (!sparkProperties.contains(k)) {
9      sparkProperties(k) = v
10    }
11  }
12}
13

4. 过滤非 Spark 属性

sparkProperties 中不以 spark. 开头的键移除掉。sparkProperties 中的值来自 --conf 和配置文件,不包含环境变量等来源。

1private def ignoreNonSparkProperties(): Unit = {
2  sparkProperties.keys.foreach { k =>
3    if (!k.startsWith("spark.")) {
4      sparkProperties -= k
5      logWarning(s"Ignoring non-Spark config property: $k")
6    }
7  }
8}
9

5. 加载环境变量参数

将命令行未指定的参数从环境变量和 sparkProperties 中补充进来。例如:master 未在命令行指定时,会依次从 spark.master 配置、环境变量 MASTER 中获取。

1private def loadEnvironmentArguments(): Unit = {
2  // Load arguments from environment variables, Spark properties etc.
3  // 从环境变量、Spark 属性等加载参数
4  master = Option(master)
5    .orElse(sparkProperties.get("spark.master"))
6    .orElse(env.get("MASTER"))
7    .orNull
8  // ... 其他参数类似
9}
10

优先级:命令行参数 > sparkProperties > 环境变量 > 默认值

6. 参数验证

验证必填参数是否存在以及参数值是否合法。例如:必须指定 primaryResource(主资源),内存/核心数必须为正数,YARN 模式必须设置 HADOOP_CONF_DIR 等。

1private def validateSubmitArguments(): Unit = {
2  // Ensure that required fields exists. Call this only once all defaults are loaded.
3  // 确保必填字段存在。调用此方法时所有默认值已加载完成
4  if (args.length == 0) {
5    printUsageAndExit(-1)
6  }
7  if (primaryResource == null) {
8    error("Must specify a primary resource (JAR or Python or R file)")
9  }
10  // ... 其他验证
11}
12

参数优先级

来源优先级
命令行参数1(最高)
--conf2
配置文件3
环境变量4
默认值5(最低)

总结

SparkSubmitArguments 的解析流程:

  1. parse - 解析命令行参数
  2. mergeDefaultSparkProperties - 合并配置文件中的属性
  3. ignoreNonSparkProperties - 过滤非 spark. 开头的属性
  4. loadEnvironmentArguments - 用环境变量和 sparkProperties 填充空缺参数
  5. validateArguments - 验证参数合法性

优先级:命令行 > --conf > 配置文件 > 环境变量 > 默认值

下一次我们将分析 Yarn Client、Yarn Cluster、Standalone Client、Standalone Cluster 模式的详细提交流程。


Spark 源码 | SparkSubmitArguments 参数解析(三)》 是转载文章,点击查看原文


相关推荐


LeetCode 459. 重复的子字符串
Best_Jerry2026/7/9

leetcode.cn/problems/re… programmercarl.com/0459.%E9%87… 给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。   示例 1: 输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。 示例 2: 输入: s = "aba" 输出: false 示例 3: 输入: s = "abcabcabcabc" 输出: true 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abc


HDFS javaAPI-windows的IDEA中java文件在linux中的hadoop平台运行
chde2Wang2026/7/1

目录 运行前提 一、在IDEA的Maven项目中创建MkDirDemo类 (1)先确认目录结构(Maven 标准目录,必须按这个来) (2)hdfs java操作代码输入 (3)验证pom.xml文件中hadoop依赖是否和linux中hadoop版本一致 (4)确认 HDFS RPC 地址(关键) 二、Windows 本地配置 Hadoop 运行环境(必做,否则报 winutils 缺失) (1)下载 Windows 适配 hadoop 二进制包Hadoop3.x 下载对应


Obsidian - 使用 Share Note 分享笔记并自部署
LinXunFeng2026/6/22

欢迎关注微信公众号:FSA全栈行动 👋 一、前言 最近在整理 Obsidian 笔记时,经常会遇到一个小需求:想把某一篇笔记快速分享给别人,但又不想为了它单独搭建博客、导出 PDF 或复制到其它平台。 如果只是分享纯文本,复制粘贴当然可以。但 Obsidian 笔记里往往会有这些内容: 图片附件 代码块 Callout 提示块 标签、任务列表 Dataview 查询结果 当前主题样式和自定义 CSS 笔记之间的内部链接 这时候手动搬运就有点麻烦了,格式容易丢,图片也要重新处理。这里介绍一


RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手
绝知此事2026/6/14

RabbitMQ 从入门到精通:Spring Boot 实战三部曲(一)—— 基础核心与快速上手 专题导读:本系列共三篇,从基础到高级,带你系统掌握 RabbitMQ 在 Spring Boot 项目中的实战应用。 第一篇:基础核心与快速上手(本文)第二篇:进阶特性与可靠性保障第三篇:高级应用与性能优化 📖 前言 在当今的分布式系统中,消息队列已成为不可或缺的基础设施。RabbitMQ 作为最流行的消息中间件之一,以其可靠性、灵活性和易用性著称。 本文将从 RabbitMQ 的基础概念出


iOS、Android、Flutter 2026 流行框架对比
王若风2026/6/7

参考文章:iOS、Android、Flutter 流行框架对比(原始链接) 我把我 2 年前的一篇博客文章进行了一次“重写”,于是有了这篇。 原文的选题很实用,结构也很清晰:按布局、网络请求、图片加载三个维度,把 iOS、Android、Flutter 常见框架放在一起横向看。 帮助移动端开发洞察各端核心框架的流行趋势,提供洞察和选项参考。 但原文的数据,放到 2026 年 6 月再看,已经有点过期了。 比如 Jetpack Compose 已经不是“新趋势”,而是 Android 新项目的


数据采集卡技术全解:从硬件架构到行业应用
zlinear数据采集卡2026/5/31

目录 数据采集卡技术全解:从硬件架构到行业应用 一、数据采集卡基础概念与分类体系 1.1 核心概念:连接物理世界与数字世界的桥梁 1.2 数据采集卡的核心功能构成 1.3 与传统测量仪器的本质区别 1.4 多维度的分类体系 1.4.1 按核心性能(采样率)分类 1.4.2 按总线接口类型分类 1.4.3 按功能与应用分类 章节小结 二、硬件架构深度解析 2.1 整体架构视图:从信号入口到数据出口 2.2 模拟前端:信号的“守门人”与“化妆师” 2.3 数据转换核心:A


技术选型决策树:什么团队、什么项目该选什么框架 | 跨平台框架深度对决(4)
陆业聪2026/5/10

跨平台框架深度对决系列 · 第4/4篇(完结篇) Flutter vs KMP vs KuiKly vs RN,谁是2026年的最优解 第1篇:跨平台框架全景图——Flutter/KMP/KuiKly/RN的2026年格局 第2篇:渲染引擎与性能拆解——自绘vs原生渲染vs Bridge的终极对决 第3篇:架构哲学与工程化——从开发体验到CI/CD的全维度对比 第4篇:技术选型决策树——什么团队、什么项目该选什么框架(本篇 · 完结) 上个月,有三个不同的朋友分别找我聊跨平台选型。 第一个是创业


RAG 系列(二):用 LangChain 搭建你的第一个 RAG Pipeline
冬奇Lab2026/4/30

从 100 行代码到生产级 Pipeline 上一篇我们用手写 Python 搭了一个最小 RAG,100 行代码跑通了核心逻辑。但如果你想把那套代码搬到生产环境,很快就会撞上一堵墙。 要加载 PDF? 你需要 PyPDF2 或 pdfplumber,然后发现表格、页眉页脚的解析是一场噩梦。 要切分文本? 你那个朴素的 text.split("\n\n") 会把句子拦腰截断、破坏代码块,或者切出超长的块直接把 Token 上限撑爆。 想换个向量数据库? 祝你下午愉快——每个数据库的 API 都不


告别 jq 噩梦!这款 JSON 神器 fx 让你在终端体验“丝滑”的数据操作
GetcharZp2026/4/21

还在为复杂的 jq 语法抓狂?antonmedv/fx 带着交互式 TUI 和纯正 JavaScript 语法来了!JSON 调试、过滤、转换,一个工具全搞定。 在程序员的日常摸鱼……哦不,日常开发中,JSON 绝对是出现频率最高的朋友。 不管是调用后端接口、查看 K8s 配置,还是分析爬虫数据,面对满屏密密麻麻、甚至没有缩进的原始 JSON 字符串,我们的第一反应通常是: 打开浏览器,搜索“JSON 在线格式化”。 把数据粘进去,点一下“美化”。 忍受网页弹窗广告,或者担心敏感数据泄露。


实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
小猿姐2026/4/13

本文基于作者在 AWS EKS 上对四款 MySQL Operator 的真实部署与测试,覆盖集群搭建、高可用切换、弹性扩缩容、动态参数、TLS 等维度,适合正在评估 MySQL Kubernetes 方案的工程师参考。 一、为什么要做这次对比测试? 过去两年,越来越多的团队开始将 MySQL 从虚拟机迁移到 Kubernetes。驱动力很直接:统一的基础设施管控、更快的弹性扩容、以及 GitOps 风格的声明式运维。 但随之而来的问题是:MySQL Operator 怎么选? 我们决定不依赖

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读