SparkCore - le meilleur emplacement pour Task

Meilleur emplacement de la tâche

  Une fois l'étape divisée dans le blog précédent, l'étape est soumise. La méthode submitMissingTasks () est utilisée pour créer un TaskSet pour chaque étape, puis le soumettre à l'exécuteur du collaborateur correspondant pour qu'il s'exécute. Analysons le processus spécifique:

 private def submitMissingTasks(stage: Stage, jobId: Int) {
    // .....
    // 获取需要创建的task的数量
    val partitionsToCompute: Seq[Int] = stage.findMissingPartitions()
    // 给stage创建一个内部累加器,暂时不知道做什么的
    if (stage.internalAccumulators.isEmpty || stage.numPartitions == partitionsToCompute.size) {
      stage.resetInternalAccumulators()
    }
	// 获取job的优先级
    val properties = jobIdToActiveJob(jobId).properties
    // 将stage加入,runningStages队列
    runningStages += stage  
    // 省略部分代码
    .....
    
    // 先计算当前stage创建的task的最佳位置
    // 针对ShuffleMapStage和ResultStage,计算它们的task的最佳位置
    val taskIdToLocations: Map[Int, Seq[TaskLocation]] = try {
      stage match {
        case s: ShuffleMapStage =>
          partitionsToCompute.map { id => (id, getPreferredLocs(stage.rdd, id))}.toMap
        case s: ResultStage =>
          val job = s.activeJob.get
          partitionsToCompute.map { id =>
            val p = s.partitions(id)
            (id, getPreferredLocs(stage.rdd, p))
          }.toMap
      }
    } catch {
      // .....
    }
    //  将创建task的广播变量,把RDD和算子函数广播到要计算的节点上去
    //  省略部分代码 .....

    // 创建指定数量的task
    val tasks: Seq[Task[_]] = try {
      stage match {
        case stage: ShuffleMapStage =>
          partitionsToCompute.map { id =>
            // 给每个partition创建一个task。
            // 获取task的最佳位置
            val locs = taskIdToLocations(id)
            val part = stage.rdd.partitions(id)
            // 创建ShuffleMapTask
            new ShuffleMapTask(stage.id, stage.latestInfo.attemptId,
              taskBinary, part, locs, stage.internalAccumulators)
          }

        case stage: ResultStage =>
          val job = stage.activeJob.get
          partitionsToCompute.map { id =>
            val p: Int = stage.partitions(id)
            val part = stage.rdd.partitions(p)
            val locs = taskIdToLocations(id)
            new ResultTask(stage.id, stage.latestInfo.attemptId,
              taskBinary, part, locs, id, stage.internalAccumulators)
          }
      }
    } catch {
      // 处理异常
     .......
    }

    if (tasks.size > 0) {
      logInfo("Submitting " + tasks.size + " missing tasks from " + stage + " (" + stage.rdd + ")")
      stage.pendingPartitions ++= tasks.map(_.partitionId)
      logDebug("New pending partitions: " + stage.pendingPartitions)
      // 最后针对stage的task,创建taskset对象,调用TaskScheduler的submitTasks()方法,提交TaskSet
      taskScheduler.submitTasks(new TaskSet(
        tasks.toArray, stage.id, stage.latestInfo.attemptId, jobId, properties))
      stage.latestInfo.submissionTime = Some(clock.getTimeMillis())
    } else {
    	// 省略代码
    }
  }

 Le code ci-dessus a deux points importants, l'un est de calculer la meilleure position de la tâche et l'autre est d'encapsuler les tâches créées par l'étape actuelle dans un TaskSet et de le soumettre au TaskScheduler pour allouer et exécuter les tâches. Regardez d'abord la méthode de calcul de l'emplacement optimal de la tâche la plus importante getPreferredLocs (), qui appelle getPreferredLocsInternal:
 

/**
    *    计算每个task对应的partition的最佳位置
    *    就是从stage的最后一个RDD开始,去找哪个RDD的partition被cache或者checkpoint了,
    *    那么task的最佳位置就是缓存或者checkpoint的partition的位置,
    *    因为这样的话task就在那个节点上执行,不需要计算之前的RDD了。
    *
    */
  private def getPreferredLocsInternal(
      rdd: RDD[_],
      partition: Int,
      visited: HashSet[(RDD[_], Int)]): Seq[TaskLocation] = {
    // partition是否已经被访问过了
    if (!visited.add((rdd, partition))) {
      // Nil has already been returned for previously visited partitions.
      return Nil
    }
    // 当前RDD的partition是否被缓存了
    val cached = getCacheLocs(rdd)(partition)
    if (cached.nonEmpty) {
      return cached
    }
    // 当前RDD的partition是否被checkpoint了
    val rddPrefs = rdd.preferredLocations(rdd.partitions(partition)).toList
    if (rddPrefs.nonEmpty) {
      return rddPrefs.map(TaskLocation(_))
    }
    // 假如上面两种情况都没有,那么递归调用自己,寻找RDD的父RDD是否被cache或checkpoint了
    // 只要有被cache或者checkpoint,那么就返回
    rdd.dependencies.foreach {
      case n: NarrowDependency[_] =>
        for (inPart <- n.getParents(partition)) {
          val locs = getPreferredLocsInternal(n.rdd, inPart, visited)
          if (locs != Nil) {
            return locs
          }
        }

      case _ =>
    }
    // 如果stage从最后一个RDD到最开始的RDD,partition都没有被缓存或checkpoint
    // 那么task就没有最佳位置,返回Nil
    Nil
  }

Le calcul de la meilleure position de la tâche ci-dessus, en général: à partir du dernier RDD de l'étape, trouvez quel RDD à cette étape est mis en cache ou checkpoint, puis la meilleure position de la tâche est la position du cache ou checkpoint, à cause de cela Si la tâche est exécutée sur ce nœud, il n'est pas nécessaire de calculer le RDD précédent. Si aucun des RDD de cette étape n'est mis en cache ou point de contrôle, alors Nil (c'est-à-dire vide) est renvoyé.
 

Je suppose que tu aimes

Origine blog.csdn.net/qq_32445015/article/details/115307963
conseillé
Classement