SparkContext初始化的流程(源码)

目录

1. SparkConf

2. SparkContext

3. TaskScheduler

4. DriverEndpoint

5. StandaloneAppClient

6. Master


1. SparkConf

  - SparkConf对象,也就是Spark的配置对象,用来描述Spark的配置信息,主要是以键值对的行式加载配置信息。
  - 一旦通过new SparkConf()完成了对象的实例化,会默认的加载spark.*配置文件。
    class SparkConf(loadDefaults: Boolean) {
        def this() = this(true)
    }

注意事项

  - SparkContext对象的实例化,需要有一个SparkConf对象作为参数。
  - 在SparkContext内部,会完成对这个SparkConf对象的克隆,得到一个各个属性值都完全相同的对象,但是和传入的SparkConf并不是同一个对象
  - 在SparkContext后续的操作中,使用到的都是这个克隆的SparkConf对象。
  ​
  - 注意事项:将SparkConf对象作为参数,传递给SparkContext对象,后续在修改这个SparkConf对象是无效的!
  ​
  /** Copy this object */
  override def clone: SparkConf = {
      val cloned = new SparkConf(false)
      settings.entrySet().asScala.foreach { e =>
          cloned.set(e.getKey(), e.getValue(), true)
      }
      cloned
  }

2. SparkContext

  SparkContext的初始化过程:
  1. 初始化了SparkConf对象,读取了默认的配置信息,并可以设置一些信息。
  2. 将SparkConf对象,加载到SparkContext中,对各个配置属性进行初始化的设置。
  3. 通过createTaskScheduler方法,实例化了TaskScheduler和DAGScheduler。
  // 根据传入的Master地址,创建SchedulerBackend和TaskScheduler
  // SparkContext.scala ,line 2692
  private def createTaskScheduler(
      sc: SparkContext,
      master: String,
      deployMode: String): (SchedulerBackend, TaskScheduler) = {
      import SparkMasterRegex._
  ​
      // When running locally, don't try to re-execute tasks on failure.
      val MAX_LOCAL_TASK_FAILURES = 1
  ​
      master match {
          // setMaster("local"),local模式
          case "local" =>
              val scheduler = new TaskSchedulerImpl(sc, MAX_LOCAL_TASK_FAILURES, isLocal = true)
              val backend = new LocalSchedulerBackend(sc.getConf, scheduler, 1)
              scheduler.initialize(backend)
              (backend, scheduler)
  ​
          // setMaster("local[2]") || setMaster("local[*]"),local模式
          case LOCAL_N_REGEX(threads) =>
              def localCpuCount: Int = Runtime.getRuntime.availableProcessors()
              // local[*] estimates the number of cores on the machine; local[N] uses exactly N threads.
              val threadCount = if (threads == "*") localCpuCount else threads.toInt
              if (threadCount <= 0) {
                  throw new SparkException(s"Asked to run locally with $threadCount threads")
              }
              val scheduler = new TaskSchedulerImpl(sc, MAX_LOCAL_TASK_FAILURES, isLocal = true)
              val backend = new LocalSchedulerBackend(sc.getConf, scheduler, threadCount)
              scheduler.initialize(backend)
              (backend, scheduler)
  ​
          // Standalone模式
          case SPARK_REGEX(sparkUrl) =>
              val scheduler = new TaskSchedulerImpl(sc)
              val masterUrls = sparkUrl.split(",").map("spark://" + _)
              val backend = new StandaloneSchedulerBackend(scheduler, sc, masterUrls)
              scheduler.initialize(backend)
              (backend, scheduler)
  ​
          // 其他的资源调度,例如Mesos、YARN
          case LOCAL_CLUSTER_REGEX(numSlaves, coresPerSlave, memoryPerSlave) =>
              // Check to make sure memory requested <= memoryPerSlave. Otherwise Spark will just hang.
              val memoryPerSlaveInt = memoryPerSlave.toInt
              if (sc.executorMemory > memoryPerSlaveInt) {
                  throw new SparkException(
                      "Asked to launch cluster with %d MB RAM / worker but requested %d MB/worker".format(
                          memoryPerSlaveInt, sc.executorMemory))
              }
  ​
              val scheduler = new TaskSchedulerImpl(sc)
              val localCluster = new LocalSparkCluster(
                  numSlaves.toInt, coresPerSlave.toInt, memoryPerSlaveInt, sc.conf)
              val masterUrls = localCluster.start()
              val backend = new StandaloneSchedulerBackend(scheduler, sc, masterUrls)
              scheduler.initialize(backend)
              backend.shutdownCallback = (backend: StandaloneSchedulerBackend) => {
                  localCluster.stop()
              }
              (backend, scheduler)
      }
  }

3. TaskScheduler

  /**
   * Low-level task scheduler interface, currently implemented exclusively by
   * [[org.apache.spark.scheduler.TaskSchedulerImpl]].
   * This interface allows plugging in different task schedulers. Each TaskScheduler schedules tasks
   * for a single SparkContext. These schedulers get sets of tasks submitted to them from the
   * DAGScheduler for each stage, and are responsible for sending the tasks to the cluster, running
   * them, retrying if there are failures, and mitigating stragglers. They return events to the
   * DAGScheduler.
   */
  TaskScheduler是一个低级别的Task调度的接口,目前只有一个实现类,就是TaskSchedulerImpl。这个TaskScheduler可以挂载在不同的调度器上(指的是SchedulerBackend)
  ​
  每一个TaskScheduler只能为一个SparkContext调度任务。初始化TaskScheduler是处理的之前的Spark的任务。如果有新的SparkApplication提交,此时就会销毁当前的TaskScheduler,并创建一个新的TaskScheduler来处理新的任务。
  ​
  TaskScheduler可以从DAGScheduler获取每一个Stage的TaskSet,用来提交处理这些Task,发到集群执行。如果失败后,会进行重复提交,处理散兵游勇。并将任务的执行结果结果反馈给DAGScheduler。
  (散兵游勇: 提交给集群运行的Task,可能会有掉队的情况,需要将这样的Task处理掉,不至于由于这一两个Task影响整体的执行。)

3.1. TaskSchedulerImpl

  客户端需要先调用initialize()和start()方法,然后才可以通过runTasks提交TaskSet
  // line81
  // Task的等待时常,默认是100ms
  val SPECULATION_INTERVAL_MS = conf.getTimeAsMs("spark.speculation.interval", "100ms")
  // line92
  // 初始化TaskSet的时常,默认是15s
  val STARVATION_TIMEOUT_MS = conf.getTimeAsMs("spark.starvation.timeout", "15s")
  // line95
  // 每一个Task分配到的CPU核数
  val CPUS_PER_TASK = conf.getInt("spark.task.cpus", 1)
  // line136
  // 调度模式,默认是FIFO
  private val schedulingModeConf = conf.get(SCHEDULER_MODE_PROPERTY, SchedulingMode.FIFO.toString)
  // CoarseGrainedSchedulerBackend
  // 粗粒度调度器(CoarseGrainedSchedulerBackend)
  //     Job的每一个生命周期中,都有一个Executor。
  //     当一个Task执行结束后,并不会立即释放Executor。
  //     当一个新的Task进来之后,不会创建一个新的Executor,会复用之前的Executor。
  //     实现了Executor的复用。
  // 
  // 细粒度调度器(FineGrainedSchedulerBackend)
  //     Task执行结束后,会释放Executor。
  //     当一个新的Task进来之后,会创建一个新的Executor去执行。
  // 
  // Standalone模式和YARN模式,只支持粗粒度调度器。
  // Mesos支持细粒度调度器。
  ​
  // 任务调度的方式:
  //     FIFO : 先进先出的调度方式
  //            优先将Executor分配到一个Worker上,当这个Worker资源不足的时候,才会将Executor分配到其他的Worker。
  //     FAIR : 公平调度
  //            基于负载均衡,平均的将Executor分配到每一个Worker节点
  def initialize(backend: SchedulerBackend) {
      this.backend = backend
      schedulableBuilder = {
        schedulingMode match {
          case SchedulingMode.FIFO =>
            new FIFOSchedulableBuilder(rootPool)
          case SchedulingMode.FAIR =>
            new FairSchedulableBuilder(rootPool, conf)
          case _ =>
            throw new IllegalArgumentException(s"Unsupported $SCHEDULER_MODE_PROPERTY: " +
            s"$schedulingMode")
        }
      }
      schedulableBuilder.buildPools()
  }
  ​
  override def start() {
      backend.start()
  ​
      if (!isLocal && conf.getBoolean("spark.speculation", false)) {
        logInfo("Starting speculative execution thread")
        speculationScheduler.scheduleWithFixedDelay(new Runnable {
          override def run(): Unit = Utils.tryOrStopSparkContext(sc) {
            checkSpeculatableTasks()
          }
        }, SPECULATION_INTERVAL_MS, SPECULATION_INTERVAL_MS, TimeUnit.MILLISECONDS)
      }
  }

3.2. StandaloneSchedulerBackend

  override def start() {
      // 调用父类CoarseGrainedSchedulerBackend中的方法实现
      // driverEndpoint = createDriverEndpointRef(properties)
      // 实例化了一个Driver的RPC通信终端
      super.start()
  ​
      // ...
      
      // 创建了一个Application的描述对象,传递了一系列参数,表示Application所需要的资源信息
      val appDesc = ApplicationDescription(sc.appName, maxCores, sc.executorMemory, command,
        webUrl, sc.eventLogDir, sc.eventLogCodec, coresPerExecutor, initialExecutorLimit)
      // 创建了一个Application的任务对象,包含了作业的资源信息
      // 用于和集群管理器进行通信
      client = new StandaloneAppClient(sc.env.rpcEnv, masters, appDesc, this, conf)
      client.start()
      launcherBackend.setState(SparkAppHandle.State.SUBMITTED)
      // 等待注册是否完成,在StandaloneAppClient完成
      waitForRegistration()
      launcherBackend.setState(SparkAppHandle.State.RUNNING)
    }

4. DriverEndpoint

  是CoarseGrainedSchedulerBackend的内部类,是Driver端的通信模型
  override def onStart() {
      // Periodically revive offers to allow delay scheduling to work
      val reviveIntervalMs = conf.getTimeAsMs("spark.scheduler.revive.interval", "1s")
  ​
      reviveThread.scheduleAtFixedRate(new Runnable {
          override def run(): Unit = Utils.tryLogNonFatalError {
              // 给自己发送一个ReviveOffers信号
              Option(self).foreach(_.send(ReviveOffers))
          }
      }, 0, reviveIntervalMs, TimeUnit.MILLISECONDS)
  }
  override def receive: PartialFunction[Any, Unit] = {
      case StatusUpdate(executorId, taskId, state, data) =>
          scheduler.statusUpdate(taskId, state, data.value)
          if (TaskState.isFinished(state)) {
              executorDataMap.get(executorId) match {
                  case Some(executorInfo) =>
                  executorInfo.freeCores += scheduler.CPUS_PER_TASK
                  makeOffers(executorId)
                  case None =>
                  // Ignoring the update since we don't know about the executor.
                  logWarning(s"Ignored task status update ($taskId state $state) " +
                             s"from unknown executor with ID $executorId")
              }
          }
  ​
      case ReviveOffers =>
          makeOffers()
  }
  // 为Executor创建虚拟的资源信息
  private def makeOffers() {
      // Make sure no executor is killed while some task is launching on it
      val taskDescs = CoarseGrainedSchedulerBackend.this.synchronized {
          // Filter out executors under killing
          val activeExecutors = executorDataMap.filterKeys(executorIsAlive)
          val workOffers = activeExecutors.map { case (id, executorData) =>
              new WorkerOffer(id, executorData.executorHost, executorData.freeCores)
          }.toIndexedSeq
          scheduler.resourceOffers(workOffers)
      }
      if (!taskDescs.isEmpty) {
          launchTasks(taskDescs)
      }
  }

5. StandaloneAppClient

  override def onStart(): Unit = {
      try {
          // 向Master发送注册信息
          // 参数的1代表第一次注册,在注册逻辑中,如果注册失败,则会将这个数字+1继续调用注册
          // 失败次数 >= 3的时候,注册失败
          registerWithMaster(1)
      } catch {
          case e: Exception =>
          logWarning("Failed to connect to master", e)
          markDisconnected()
          stop()
      }
  }
  private def registerWithMaster(nthRetry: Int) {
      registerMasterFutures.set(tryRegisterAllMasters())
      registrationRetryTimer.set(registrationRetryThread.schedule(new Runnable {
          override def run(): Unit = {
              if (registered.get) {
                  registerMasterFutures.get.foreach(_.cancel(true))
                  registerMasterThreadPool.shutdownNow()
              } else if (nthRetry >= REGISTRATION_RETRIES) {
                  markDead("All masters are unresponsive! Giving up.")
              } else {
                  registerMasterFutures.get.foreach(_.cancel(true))
                  registerWithMaster(nthRetry + 1)
              }
          }
      }, REGISTRATION_TIMEOUT_SECONDS, TimeUnit.SECONDS))
  }

6. Master

  // line 258
  // 在receive方法中,用来接收Driver端发送过来的消息,进行模式匹配
  ​
  case RegisterApplication(description, driver) =>
      // TODO Prevent repeatd registrations from some driver
      if (state == RecoveryState.STANDBY) {
          // ignore, don't send response
      } else {
          logInfo("Registering app " + description.name)
          // 创建应用程序,封装对应的Driver端的资源
          val app = createApplication(description, driver)
          // 在Master的内部完成Appliction的注册
          registerApplication(app)
          logInfo("Registered app " + description.name + " with ID " + app.id)
          // 使用持久化的操作,将任务的元信息保存,以便Task使用
          persistenceEngine.addApplication(app)
          // 告诉Driver端,注册完成!
          driver.send(RegisteredApplication(app.id, self))
          schedule()
      }

StandaloneAppClient

  override def receive: PartialFunction[Any, Unit] = {
      case RegisteredApplication(appId_, masterRef) =>
          // FIXME How to handle the following cases?
          // 1. A master receives multiple registrations and sends back multiple
          // RegisteredApplications due to an unstable network.
          // 2. Receive multiple RegisteredApplication from different masters because the master is
          // changing.
          appId.set(appId_)
          registered.set(true)
          master = Some(masterRef)
          listener.connected(appId.get)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值