前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住给大家分享一下。点击跳转到网站:https://www.captainai.net/dongkelun
前言
在第二篇文章 Spark 源码 | SparkSubmit 提交流程分析(二) 中,我们分析了 SparkSubmit 的提交流程。在 Yarn Client 模式下:
runMain方法会调用app.start(Yarn Client 模式下为 JavaMainApplication),在 app.start 中通过反射调用用户主类的 main 方法(具体调用过程在不同模式中有所不同,后续分析各模式时再详细说明)- 用户主类(如 SparkPi)的 main 方法通常会创建 SparkSession 或 SparkContext
- 在创建 SparkContext 时,会创建 TaskScheduler 和 SchedulerBackend
本文说明:虽然本文以 Yarn Client 模式为例(YarnScheduler、YarnClientSchedulerBackend),但 SparkContext 初始化和 TaskScheduler 创建流程是各模式的公共前提。后续分析 Yarn Cluster、Standalone 等模式时可直接参考本篇内容。
为什么要先写这篇:TaskScheduler 和 SchedulerBackend 的创建是各模式的公共前提,内容较多。如果不先单独分析,在分析各模式提交流程时就需要同时包含这些内容,导致每篇文章都很长。因此本文先专门分析它们的创建过程,后续分析各模式时就可以直接聚焦于该模式特有的流程(如 Executor 启动)。
版本
Spark 3.2.3
SparkContext 初始化入口
用户代码通常这样创建 SparkContext:
1 | val spark = SparkSession.builder() |
SparkSession.getOrCreate
1 | def getOrCreate(): SparkSession = synchronized { |
可以看到 SparkSession.getOrCreate() 内部调用的是 SparkContext.getOrCreate(sparkConf)。
SparkContext.getOrCreate
1 | def getOrCreate(config: SparkConf): SparkContext = { |
最终会创建 new SparkContext(config)。
new SparkContext(config)
在 SparkContext 构造函数中,会进行大量的初始化工作,关键步骤如下:
1. 验证配置(master、app.name)
1 | if (!_conf.contains("spark.master")) { |
2. 初始化环境、内存、序列化器等
1 | // 创建 Spark 执行环境 |
3. 注册 HeartbeatReceiver
1 | // 必须在 createTaskScheduler 之前注册 |
4. 创建 TaskScheduler、SchedulerBackend 和 DAGScheduler
1 | val (sched, ts) = SparkContext.createTaskScheduler(this, master, deployMode) |
5. 启动 TaskScheduler
1 | _taskScheduler.start() |
整个初始化过程在 try-catch 块中,异常时调用 stop() 方法确保资源释放。
createTaskScheduler 方法
createTaskScheduler 是 SparkContext 内部方法,根据 master URL 和 deployMode 创建对应的 TaskScheduler 和 SchedulerBackend。
1 | private def createTaskScheduler( |
getClusterManager 加载外部集群管理器
对于 YARN、Kubernetes 等模式,通过 Java ServiceLoader 机制加载 ExternalClusterManager 实现类:
1 | private def getClusterManager(url: String): Option[ExternalClusterManager] = { |
ServiceLoader 机制:Spark 使用 Java SPI(Service Provider Interface)机制加载外部集群管理器。在对应模块的 META-INF/services 目录下创建文件。
例如 YARN 模块的配置文件(resource-managers/yarn/src/main/resources/META-INF/services/org.apache.spark.scheduler.ExternalClusterManager):
1 | org.apache.spark.scheduler.cluster.YarnClusterManager |
当 master = “yarn” 时,getClusterManager("yarn") 会加载 YarnClusterManager。这是因为:
ServiceLoader.load()加载所有实现了ExternalClusterManager接口的类.filter(_.canCreate(url))过滤,只保留canCreate返回true的- 当
url = "yarn"时:YarnClusterManager.canCreate("yarn")→true(声明能管理 yarn)- 其他集群管理器(Mesos、K8s)的
canCreate("yarn")→false
- 过滤后只剩下
YarnClusterManager,返回它
Yarn 模式的集群管理器
YarnClusterManager
当 master = “yarn” 时,加载的是 YarnClusterManager,它根据 deployMode 创建不同的 TaskScheduler 和 SchedulerBackend:
1 | class YarnClusterManager extends ExternalClusterManager { |
Yarn Client 模式创建的类
| 组件 | 类名 |
|---|---|
| TaskScheduler | YarnScheduler(继承自 TaskSchedulerImpl) |
| SchedulerBackend | YarnClientSchedulerBackend |
YarnClientSchedulerBackend 继承关系:
1 | YarnClientSchedulerBackend |
SparkContext 初始化 TaskScheduler
在 SparkContext 构造函数中,创建 TaskScheduler 后会调用 start() 方法:
1 | // 创建并启动 scheduler |
TaskScheduler.start() 调用链
对于 Yarn Client 模式,整个调用链如下:
1. YarnClusterManager 创建的对象:
1 | // createTaskScheduler 创建 |
2. SparkContext 启动:
1 | _taskScheduler.start() |
3. YarnScheduler.start():
YarnScheduler 没有重写 start() 方法,继承自 TaskSchedulerImpl:
1 | // TaskSchedulerImpl.start() |
4. YarnClientSchedulerBackend.start():
最终会调用 YarnClientSchedulerBackend.start(),主要完成:
- 创建 Client 对象
- 通过
client.submitApplication()向 YARN RM 提交 Application - 通过
bindToYarn()绑定 appId - 等待 Application 运行
关于 YarnClientSchedulerBackend.start() 的详细源码分析,将在下一篇文章《Yarn Client 模式提交流程》中详细介绍。
关键源码(详细分析见下一篇文章):
1 | override def start(): Unit = { |
完整流程图
1 | 用户代码 |
总结
本文分析了 SparkContext 初始化和 TaskScheduler 创建流程(以 Yarn Client 模式为例):
- SparkSession.getOrCreate() → 创建 SparkContext
- SparkContext 构造函数 → 调用 createTaskScheduler
- createTaskScheduler → 根据 master 和 deployMode 创建对应实现
- ClusterManager → 通过 SPI 加载对应的 ExternalClusterManager(如 YarnClusterManager),根据 deployMode 创建:
- client 模式:YarnScheduler + YarnClientSchedulerBackend
- cluster 模式:YarnClusterScheduler + YarnClusterSchedulerBackend
- TaskScheduler.start() → 启动 SchedulerBackend,与集群管理器通信
SparkContext 初始化是所有部署模式(Yarn Client/Cluster、Standalone 等)的共同前置流程,理解此流程后分析各模式的具体提交流程将更清晰。
下一次
下一次我们将分析 Yarn Client 模式的详细提交流程,包括 YarnClientSchedulerBackend.start() 的详细分析以及 Executor 启动流程。