整个spark应用程序的运行分成三个阶段:
1、编写代码,使用spark-submit去交任务到集群运行,一直到我们自己编写的main方法运行为止
1、编写代码
2、打成jar
3、编写sprak-submit脚本提交任务
4、脚本解析和执行最终转到main方法执行
SparkSubmit
2、sparkContext的初始化
new SparkContext(sparkConf)
两条线:
1、在driver端执行的各种代码
491-500行:
初始化了 TaskScheduler -----> TaskSchedulerImpl
初始化了 SchedulerBackend -----> StandAloneSchedulerBackend
初始化了 DAGScheduler -----> DAGScheduler
209-210
1758
2、在worker和master端执行的各种代码
业务功能实现:
1、master注册
2、worker负责启动executor
SparkContext中 500:
_taskScheduler.start()
TaskSchedulerImpl中164:
backend.start()
这个方法有两个重点:
1、super.start()
driverActor
创建:
CoarseGrainedSchedulerBackend中的391行:
driverEndpoint = createDriverEndpointRef(properties)
2、114-120
clientActor
创建:
(如果说是看的spark-1.x(RPC: akka)的源码,当前这两个对象:
clientActor: spark2.x(RPC:netty)中的 StandaloneAppClient中的 ClientEndPoint
EndPoint的概念等同于了Spark1.x中的Actor
EndPoint是netty中的概念
Actor是AKKA中的概念
)
actor receive preStart
endPoint receive onStart
转到 StandAloneAppClinet类中的 86:
// 开始注册
registerWithMaster(1)
转到124行:
tryRegisterAllMasters()
转到107行:
masterRef.send(RegisterApplication(appDescription, self))
masterRef: Spark集群的主节点:master中的其中一个actor
转到Master这个类中的259行:
case RegisterApplication(description, driver)
重点有三句代码:
266行:registerApplication(app)
269行:driver.send(RegisteredApplication(app.id, self))
270行: schedule()
作用就是调度资源,然后在worker中启动executor
schedule()执行后转到735行:
两个重点:
1、launchDriver(worker, driver)
启动ExecutorBackend
其实launchDriver中的driver就是这个对象:
CoarseGrainedExecutorBackend
2、startExecutorsOnWorkers()
转到682行:
分配资源给Executor,启动Executor
转到708行:
launchExecutor(worker, exec)
启动和加载运行Executor
转到Worker类的479行:
LaunchExecutor 真正启动Executor的逻辑代码
转到532行:
manager.start()
转到ExecutorRunner中的73行:
fetchAndRunExecutor()
最终拼装了一个java -Xmx1000M Executor
启动了Executor
初始化Executor的时候:
两个重点:
1、初始化线程池
Executor中89行:
private val threadPool = {
Executors.newCachedThreadPool(threadFactory).asInstanceOf[ThreadPoolExecutor]
}
如果Executor对象创建成功了,就意味着线程池也初始化成功了。
那就意味着:Exzecutor启动成功。
所以等到任务提交过来即可
在executor启动成功之后,进行反向注册:
executor 向 driverActor 注册
从Master中的:748行, executor都启动成功
当Executor启动成功之后:
750行:
exec.application.driver.send(ExecutorAdded)
注册到clientActor之上
----------------------到此为止,一个Executor启动成功------------------------------
----------------------当所有的executor都启动成功了之后,整个sparkContext对象初始化成功------------------------------
2、等到任务的提交
174行-178行:
def launchTask(context: ExecutorBackend, taskDescription: TaskDescription): Unit = {
val tr = new TaskRunner(context, taskDescription)
runningTasks.put(taskDescription.taskId, tr)
threadPool.execute(tr)
}
3、action算子触发任务的提交和执行:sc.runJob()
collect
当一个程序中,碰到第一个actoin算子的时候,就相当于触发了 sc.runJob方法执行
转到:
SparkContext中的2034行:
dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get)
转到DAGScheduler中的632行:
val waiter = submitJob(rdd, func, partitions, callSite, resultHandler, properties)
转到604行:
eventProcessLoop.post(JobSubmitted)
转到1829行:
dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties)
三个重点:
1、867行:切分stage
finalStage = createResultStage
2、激活job
val job = new ActiveJob(jobId, finalStage, callSite, listener, properties)
3、提交stage
submitStage(finalStage)
转到947行提交任务:
submitMissingTasks(stage, jobId.get)
转到1088行: 是在DAGScheduler当中调用的。
taskScheduler.submitTasks(new TaskSet(
tasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties))
但是最后:
DAGScheduler的submitMissingTasks方法就把stage变成了一个TaskSet对象
最终转到
taskScheduler中的submitTasks执行
要点核心结论:
DAGScheduler会给一个job切分成多个stage
然后把每一个stage提交给taskScheduler去执行
DAGScheduler的工作:
从action算子开始:
到把stage变成TaskSet提交到taskScheduler中去执行结束
从TaskSchedulerImpl类中的submitTasks方法(180行)去分析:
重点代码:最后一句:
213行:
backend.reviveOffers()
转到:
CoarseGrainedSchedulerBackend类中的136行:
makeOffers()
转到251行:
launchTasks(taskDescs)
转到CoarseGrainedExecutorBackend中的98行:
executor.launchTask(this, taskDesc)
转到Executor中的177行:
threadPool.execute(tr)
sparkContext.stop();
等到线程池接收到所有的任务之后,以此执行成功,最终整个Application执行成功