-1- .Driver 启动的过程
1.spark-submit 脚本,在脚本里调用了org.apache.spark.deloy.SparkSubmit 类
2.SparkSubmit.scala main方法
override def main(args: Array[String]): Unit = {
val appArgs = new SparkSubmitArguments(args)
if (appArgs.verbose) {
// scalastyle:off println
printStream.println(appArgs)
// scalastyle:on println
}
appArgs.action match {
case SparkSubmitAction.SUBMIT => submit(appArgs)
case SparkSubmitAction.KILL => kill(appArgs)
case SparkSubmitAction.REQUEST_STATUS => requestStatus(appArgs)
}
}
3.SparkSubmit.scala submit(appArgs)方法
private def submit(args: SparkSubmitArguments): Unit = {
val (childArgs, childClasspath, sysProps, childMainClass) = prepareSubmitEnvironment(args)
if (args.isStandaloneCluster && args.useRest) {
try {
// scalastyle:off println
printStream.println("Running Spark using the REST application submission protocol.")
// scalastyle:on println
doRunMain()
} catch {
// Fail over to use the legacy submission gateway
case e: SubmitRestConnectionException =>
printWarning(s"Master endpoint ${args.master} was not a REST server. " +
"Falling back to legacy submission gateway instead.")
args.useRest = false
submit(args)
}
// In all other modes, just run the main class as prepared
} else {
doRunMain()
}
}
4.SparkSubmit.scala runMain()
该方法主要是 确定mainClass,使用classFromName,获取类对象,然后采用映射调用main方法
本次假设mainClass 为Client.scala,解释:Application提交过程
5.Client main方法
在main方法中,new ClientEndPoint 对象创建过程中,会向Master发送RegisterDriver消息。
object Client {
def main(args: Array[String]) {
// scalastyle:off println
if (!sys.props.contains("SPARK_SUBMIT")) {
println("WARNING: This client is deprecated and will be removed in a future version of Spark")
println("Use ./bin/spark-submit with \"--master spark://host:port\"")
}
// scalastyle:on println
val conf = new SparkConf()
val driverArgs = new ClientArguments(args)
if (!conf.contains("spark.rpc.askTimeout")) {
conf.set("spark.rpc.askTimeout", "10s")
}
Logger.getRootLogger.setLevel(driverArgs.logLevel)
val rpcEnv =
RpcEnv.create("driverClient", Utils.localHostName(), 0, conf, new SecurityManager(conf))
val masterEndpoints = driverArgs.masters.map(RpcAddress.fromSparkURL).
map(rpcEnv.setupEndpointRef(_, Master.ENDPOINT_NAME))
rpcEnv.setupEndpoint("client", new ClientEndpoint(rpcEnv, driverArgs, masterEndpoints, conf))
rpcEnv.awaitTermination()
}
}
6. 之后就是Driver 启动,SparkContext初始化的过程了
-2- .Executor启动相关
1.Master shchedula()方法中,worker.endPoint.send(LaunchExecutor)请求的时候,同步发送了一份信息给app.driver: exec.application.driver.send(ExecutorAdded) ,所以在ExecutorBackEnd启动向Driver注册之前,Driver就已经知道了该executor的存在,只不过状态还未更改。
2.Worker 在启动Executor的时候,先new ExecutorRunner,runner不是进程也不是线程,只是一个对象,在runner.start()中,使用线程异步 启动了一个线程,该线程用于启动executorBackEnd.
3.executor运行结束之后,使用backend.updateStatus() 向schedulerBackEnd 发送消息,schedulerBackEnd 的receive中,会把结果交给taskSchedular进行处理,然后按照处理的结果在进行相关操作,比如,如果运行成功不需要重试,那么schedulerBackEnd,就会把cores加到freeCores中,然后调用makeOffers() 重新进行task的资源分配,看有没有满足资源条件的task可以运行。
-3- .任务调度相关
job提交之后,调用runjob,到最终task被分配到executor之前所涉及到的调度相关
1.首先涉及到的调度是job stage 划分和提交过程,也就是submitStage方法,所有又依赖的Satge,也就是说有父Satge的子Stage,子Stage调用submitSatge的时候,会将子Satge添加到watingSatge队列中,换句话说,如果一个Stage有父依赖,那么他就不能被subnitMissingSatge submit,会被加入到watingSatge,只有没有依赖的Satge才会被提交。
没有依赖的Stage提交,会将Satge转换成tasksetManager,提交给TaskScheduar
2.taskSchedular在初始化的时候,方法位于sparkcontext中,初始化的时候初始化了一个队列,这个队列有两个选择:FIFO/FAIR,
tasksetManager提交给taskSchedular的时候就会加入到该队列中,比如FIFO队列,有两层排序,一层是根据jobid,jobid越小的优先级越高,同一job内部,存在第二层排序,stageid,stageid越小的优先级越高
值得足以的一点就是:stage提交的时候,有依赖,就不会添加到队列中,会加入到watingSatge中,等待某一个stage完成之后,会检查watingSatge提交已经没有依赖的Stage