[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务

                    spark源码学习(八):spark具体是如何使用集群的资源去运行任务


     在前面的blog中谈到了sparkContext,DAGScheduler的初始化,TaskSeceduler的启动。Driver,Master,

Worker,Executor相关的注册和消息传递。还有resultStage,ActiveJob的创建以及stage的提交,划分,

ShuffleMapTask和ResultTasks的创建。前一部分是和spark集群的初始化和后台schedulerBackend调度模式的选

择,后一部分是和任务的运行相关的工作,但是,两者之间到底是如何去进行链接呢?也就是说,在集群启动之后,

我们去写一部分相关的RDD的操作,当遇到rdd的action操作要runJob的时候,是如何获取worker上的资源然后进行运

行任务的呢?下面我们来大体的介绍一下相关的代码。

     在sparkContext的初始化分析中,在最后仅仅看到了这篇代码:


       我们以standalone模式来看看具体的代码实现,进入CoarseGrainedSchedulerBackend的start方法:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第1张图片

       最前的for代码块儿就是找出以在配置文件中以"spark"字符串开头的配置,因为在standalone下的配置都是以spark

抬头的。在添加完属性之后,就开始创建一个driverActor,这就是task和集群链接的核心之处

     在stage的划分和task的创建的时候,在最后一段这样的代码:


  进入tashScheduleImpl的submitTasks的具体实现来看看:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第2张图片

         看到了backend.reviveOffers函数,它主要是进行资源分配使用的,就是索取资源。因为你要想提交任务,总

得需要申请任务需求的资源吧。进入函数具体看看。这里我们已然进入 CoarseGrainedSchedulerBackend的revive-

offers函数:


      既然发送了一个reviveOffers消息,那就去找找对应的接收消息,如下:


     进入makeOffers函数去看看:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第3张图片

      这里的首先把ececutorDataMap中的executorData转化成workerOffer。然后调用TaskSchedulerImpl的resource

Offers函数给当前的任务分配Executor,resourceOffers主要是用于task的资源分配,resourceOffer用于给Worker分

配task。那就进如LaunchTasks函数看看:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第4张图片

      executorActor发送的LaunchTask消息,那就去看看接收到之后如何处理:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第5张图片

          红线圈起来的代码应该在任务的提交和运行里面有看到。

           其实,这里还有一个问题executor是任务执行的核心,但是executor是如何创建的呢?虽然我们在任务的提交

和运行那儿看到在worker接收到master的registeredExecutor消息之后会创建一个Executor.但是我们在分析这篇代码

的时候是直接从 CoarseGrainedExecutorBackend直接去分析的,那么问题就是:在stanalone的cluster中,什么时候

用到了这个 CoarseGrainedExecutorBackend的呢?下面就讨论这个CoarseGrainedExecutorBackend的创建,这个

class的确有点儿不容易发现。在stanalone源码分析的时候,我们在片尾分析到了LaunchExecutor函数:


       workerActor在接收到LaunchExecutor消息之后会创建一个ExecutorRunner类。首先,创建executor的工作目录

创建App的本地目录,在app完成的时候会自动删除,创建并启动ExecutorRunner,向master发送ExecutorStateCha

nged消息。在启动executorRunner之后,会创建workerThread县城个shutdownHook县城,后者用于在worker关闭

时候杀死所有的Executor进程。而workerThread主要是调用fetchAndRunExecutor方法。如下所示:

[置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务_第6张图片

       在debug的模式下,会看到sun.java.command正是CommandUtils.buildProcessBuilder构建的command。具体是

sun.java.command=org.apache.spark.exexutor.CoarseGrainedExecutorBackend ......那么就当然进入这个类的main

方法去看看:

private def run() {
      val executorConf = new SparkConf
      val port = executorConf.getInt("spark.executor.port", 0)
	  //第一步:给driver发送driverPropsFetcher消息获取spark属性
      val fetcher = RpcEnv.create("driverPropsFetcher",hostname,port,executorConf,new SecurityManager(executorConf))
      ......
      // Create SparkEnv using properties we fetched from the driver.
      ......
	  //第二步:使用spark属性创建自身需要的ActorSystem这里本质上就相当于创建了ActorSystem
	  //这里本质上就相当于创建了ActorSystem
      val env = SparkEnv.createExecutorEnv(
        driverConf, executorId, hostname, port, cores, isLocal = false)
      // SparkEnv sets spark.driver.port so it shouldn't be 0 anymore.
      val boundPort = env.conf.getInt("spark.executor.port", 0)
      assert(boundPort != 0)
      // 开启 CoarseGrainedExecutorBackend 的Actor.
      val sparkHostPort = hostname + ":" + boundPort
	  //第三步:注册CoarseGrainedExecutorBackend到ActorSystem
      env.rpcEnv.setupEndpoint("Executor", new CoarseGrainedExecutorBackend(//看到了吧
        env.rpcEnv, driverUrl, executorId, sparkHostPort, cores, userClassPath, env))
	  //第四步:注册WorkerWatcher到ActorSystem中
      workerUrl.foreach { url =>
        env.rpcEnv.setupEndpoint("WorkerWatcher", new WorkerWatcher(env.rpcEnv, url))
      }
      env.rpcEnv.awaitTermination()
      SparkHadoopUtil.get.stopExecutorDelegationTokenRenewer()
    }
  }
        重点来了,在注册CoarseGrainedExecutorBackend会触发 onstart方法,去看看吧:


  主要是向DriverActor发送RegisterExecutor消息,DriverActor接到RegisteredExecutor消息后处理的流程已经很

详细的介绍过了。至此,spark的集群的初始化和任务的提交的大致框架就说完啦。仅仅是大致的框架而已,具体的

函数分析以后再说吧。最后贴一张某大神画的用例图会更加帮助理解:



你可能感兴趣的:([置顶] spark源码学习(八):spark具体是如何使用集群的资源去运行任务)