Spark Task级调度及黑名单和失败重试

Spark 在执行用户app程序时,执行Driver时会存在TaskScheduler 任务调度器属性,这个属性在伴随Driver的执行就会初始化,在StageScheduler 中 submitTask任务时,把任务提交给TaskScheduler中

TaskScheduler:Driver中的task任务调度器

在Driver执行过程中,DAGScheduler将划分的阶段Stage提交给TaskScheduler 后,TaskScheduler 将Stage 按照最后一个RDD的分区数量来划分成task,包装成TaskSet,再将TaskSet封装成TaskSetManager,将TaskSetManager加入TaskScheduler 的任务队列中

 if (tasks.nonEmpty) {
      logInfo(s"Submitting ${tasks.size} missing tasks from $stage (${stage.rdd}) (first 15 " +
        s"tasks are for partitions ${tasks.take(15).map(_.partitionId)})")
      taskScheduler.submitTasks(new TaskSet(
        tasks.toArray, stage.id, stage.latestInfo.attemptNumber, jobId, properties))
    } else {
      // Because we posted SparkListenerStageSubmitted earlier, we should mark
      // the stage as completed here in case there are no tasks to run
      markStageAsFinished(stage, None)

      stage match {
        case stage: ShuffleMapStage =>
          logDebug(s"Stage ${stage} is actually done; " +
              s"(available: ${stage.isAvailable}," +
              s"available outputs: ${stage.numAvailableOutputs}," +
              s"partitions: ${stage.numPartitions})")
          markMapStageJobsAsFinished(stage)
        case stage : ResultStage =>
          logDebug(s"Stage ${stage} is actually done; (partitions: ${stage.numPartitions})")
      }
      submitWaitingChildStages(stage)
    }

封装成tasksetManager

override def submitTasks(taskSet: TaskSet): Unit = {
    val tasks = taskSet.tasks
    logInfo("Adding task set " + taskSet.id + " with " + tasks.length + " tasks")
    this.synchronized {
      val manager = createTaskSetManager(taskSet, maxTaskFailures)
      val stage = taskSet.stageId
      val stageTaskSets =
        taskSetsByStageIdAndAttempt.getOrElseUpdate(stage, new HashMap[Int, TaskSetManager])

      // Mark all the existing TaskSetManagers of this stage as zombie, as we are adding a new one.
      // This is necessary to handle a corner case. Let's say a stage has 10 partitions and has 2
      // TaskSetManagers: TSM1(zombie) and TSM2(active). TSM1 has a running task for partition 10
      // and it completes. TSM2 finishes tasks for partition 1-9, and thinks he is still active
      // because partition 10 is not completed yet. However, DAGScheduler gets task completion
      // events for all the 10 partitions and thinks the stage is finished. If it's a shuffle stage
      // and somehow it has missing map outputs, then DAGScheduler will resubmit it and create a
      // TSM3 for it. As a stage can't have more than one active task set managers, we must mark
      // TSM2 as zombie (it actually is).
      stageTaskSets.foreach { case (_, ts) =>
        ts.isZombie = true
      }

加入队列中

stageTaskSets.foreach { case (_, ts) =>
        ts.isZombie = true
      }
      stageTaskSets(taskSet.stageAttemptId) = manager
      schedulableBuilder.addTaskSetManager(manager, manager.taskSet.properties)

      if (!isLocal && !hasReceivedTask) {
        starvationTimer.scheduleAtFixedRate(new TimerTask() {
          override def run(): Unit = {
            if (!hasLaunchedTask) {
              logWarning("Initial job has not accepted any resources; " +
                "check your cluster UI to ensure that workers are registered " +
                "and have sufficient resources")
            } else {
              this.cancel()
            }
          }
        }, STARVATION_TIMEOUT_MS, STARVATION_TIMEOUT_MS)
      }
      hasReceivedTask = true
    }
    backend.reviveOffers()
  }

然后根据调度把任务发送到Excetor中,Excetor中有taskpool,Excetor中的excetor对象就会从任务池中拿到这些任务执行

TaskScheduler 中的调度策略有两种,一种是FIFO,另一种是FAIR ,默认是FIFO

失败重试与黑名单:
当Excetor中的excetor对象执行task任务失败时,会把信息反馈给TaskScheduler,taskScheduler把信息反馈给TaskSetManager,TaskSetManager把task重新提交到taskpool中,同时记录失败的excetor,加如黑名单,这样下次执行就不会提交到此Excetor上了,但是在提交多次后还是失败,则整个application失败

Spark任务在Task阶段失败时具备自动重试机制。当某个Task执行失败时,Spark调度系统会自动尝试重新执行该Task,最多尝试的次数由配置参数`spark.task.maxFailures`控制,默认值为4次。这意味着如果一个Task失败超过这个次数,整个Stage将被标记为失败,进而可能触发Stage别的重试机制[^2]。 在Task失败后,Spark调度器(TaskSchedulerImpl)会尝试在其他可用的Executor上重新执行该Task,以确保任务的容错性。Task失败重试机制主要适用于由于Executor失败、网络问题或临时性错误导致的异常,而不适用于用户代码中的逻辑错误或数据质量问题。在这些情况下,需要手动调整代码或数据,以避免Task持续失败[^2]。 ### Task重试与Stage重试的关系 Task失败重试是Stage失败重试机制的一部分。如果某个Task在达到`spark.task.maxFailures`限制后仍然失败Spark将认为该Stage失败,并根据`spark.stage.maxConsecutiveAttempts`配置的参数尝试重新执行整个Stage。Stage的最大连续重试次数默认为4次,超过该次数后,整个作业将被标记为失败。 ### 配置Task重试机制 可以通过以下方式配置Task重试机制: ```scala val conf = new SparkConf() .set("spark.task.maxFailures", "5") // 设置Task的最大失败次数为5次 val sc = new SparkContext(conf) ``` 上述配置将Task的最大失败次数设置为5次,适用于任务执行过程中失败概率较高的场景。 ### Spark的推测执行机制 除了Task的自动重试外,Spark还支持推测执行(Speculative Execution),即在某些Task执行较慢时,调度器会启动该Task的副本,以避免慢任务影响整体执行效率。推测执行的间隔由`spark.speculation``spark.speculation.interval`控制,默认情况下,推测执行是启用的,且每100毫秒检查一次是否需要启动推测任务[^3]。 以下代码片段展示了TaskSchedulerImpl中启动推测执行线程的逻辑: ```scala 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) } } ``` ### Task重试机制的适用场景 Task重试机制适用于以下场景: - **Executor失败**:当某个Executor因故障退出时,其上运行的Task会自动重试。 - **网络问题**:任务因网络中断或超时失败时,Spark会尝试重新调度该任务。 - **临时性错误**:如资源不足、外部依赖服务短暂不可用等情况。 然而,Task重试机制并不适用于以下情况: - **代码逻辑错误**:如果任务因代码逻辑错误(如空指针异常、类型转换错误)失败重试不会解决问题。 - **数据质量问题**:如果任务因输入数据异常(如格式错误、缺失字段)失败重试也无法解决。 ### 示例配置 ```bash spark-submit --conf spark.task.maxFailures=5 \ --conf spark.speculation=true \ --class org.apache.spark.examples.SparkPi \ --master yarn \ --deploy-mode cluster \ ./spark-examples_2.11-2.4.7.jar ``` 上述配置启用了Task的自动重试推测执行机制,适用于集群环境较为不稳定、任务失败概率较高的场景。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值