spark-core_27: DAGScheduler的初始化源码分析

29 篇文章 4 订阅

1,SparkContext在初始完成SparkDeploySchedulerBacked和TaskSchedulerImpl,就开始初始化DAGScheduler,并将

TaskSchedulerImpl、MapOutputTrackerMaster、BlockManagerMaster,设置到它的成员上

  * 1,DAGScheduler面向stage的高级调度层。 它计算每个Job的DAG(一个action会有一个job,而一个job有多个stage),跟踪所有RDD和shuffleMapStage的输出位置,找到最优化的DAG图进行执行。
  * DAGScheduler会将每个stage的TaskSets(一个stage就对应一个TaskSet)提交到TaskSchedulerImpl,放到集群上运行。
  *
  * 2,TaskSet包含完全独立的任务,这些任务可以基于群集中已有的数据(如:来自前一个shuffleMagStage输出文件),然后立即运行。
  *
  *
  * 3,每一次shuffle就是一个stage。每个stage包括多个窄依赖的Rdd操作,(如map()和filter()),这些窄依赖操作会放在一个pipeline在这个Stage中
  *
  * 4,如果是shuffle依赖操作会对应多个stage(父stage负责shufflewriter输出文件数据,子stage负责shuffle reader读取文件数据)。
  *
  * 5,DAGScheduler除了提供每个stage对应DAG外,DAGScheduler每个task对应的数据本地性,并将这些数据位置传递给底层的TaskScheduler。
  * 此外,它还会处理,由于shuffle输出文件丢失,会让以前stage重新提交执行。
  * 如果失败不是因为shuffle文件丢失,会让TaskScheduler来处理,TaskScheduler会重试每个任务,而不是取消整个阶段。

  *关键名词:
  * - Jobs(由
[[ActiveJob]]表示)是action触发的,是给调度使用的。
  * 例如,当用户调用某个action动作时,如count(),job的提交会通过submitJob。一个job对应多个stage来构建中间数据。
  *
  * - Stages(
[[Stage]]一组task的集合,给Job计算中间结果用的。
  * 1,在相同的Rdd分区上,每个task计算的RDD函数是一样的。
  * 2,每个stage在shuffle时分开,这引入了barrier(我们必须等待前一阶段完成获取输出)。
  * 3,有两种类型的阶段:执行action的最后Stage称为
[[ResultStage]]和父shuffler的map称为[[ShuffleMapStage]]
  * 4,如果这些作业重复使用相同的RDD,则阶段通常跨多个作业共享。
  *
  * - Tasks是工作的基本单位,每个task都发送到一台机器。
  * - Cache tracking:DAGScheduler计算出哪些RDD被缓存以避免重新计算它们,并同样记住哪些ShuffleMapStage已经产生了输出文件,以避免重新ShuffleMapStage。
  * - Preferred locations:DAGScheduler还根据其基础RDD的首选位置或缓存或随机数据的位置计算在某个阶段中运行每个任务的位置。
  * - Cleanup:当依赖于它们的正在运行的作业完成时,将清除所有数据结构,以防止长时间运行的应用程序中发生内存泄漏。
  *
  * 要从故障中恢复,同一个stage可能需要多次运行,这称为“尝试”。比如:TaskScheduler报告由于前一阶段的ShuffleMapStage输出文件丢失而导致任务失败,会重试stage,
  * 则DAGScheduler会重新提交丢失的阶段。这可通过带有FetchFailed的CompletionEvent或ExecutorLost事件来检测。
  * DAGScheduler将等待少量时间来查看其他节点或任务是否失败,然后重新提交TaskSets以查找丢失的计算丢失任务的阶段。
  * 作为这个过程的一部分,我们可能还必须为之前清理过Stage对象的旧(已完成)阶段创建Stage对象。 由于阶段的旧尝试的任务仍可以运行,
  * 因此必须小心地映射在正确的阶段对象中接收到的任何事件。
  *
  * 以下是制作或查看这个类时,所做更改时使用的清单:
    - 当涉及它们的作业结束时,应该清除所有的数据结构,以避免长时间运行的程序无限期地累积状态。
    - 添加新的数据结构时,更新
`DAGSchedulerSuite.assertDataStructuresEmpty`以包含新的结构。这将有助于捕捉内存泄漏。
  *
 */

private[spark]
class DAGScheduler(
    private[scheduler] val sc: SparkContext,
    private[scheduler] val taskScheduler: TaskScheduler,
    listenerBus: LiveListenerBus,
    mapOutputTracker: MapOutputTrackerMaster,
    blockManagerMaster: BlockManagerMaster,
    env: SparkEnv,
    clock: Clock = new SystemClock())
  extends Logging {

  def this(sc: SparkContext, taskScheduler: TaskScheduler) = {
    this(
      sc,
      taskScheduler, //TaskSchedulerImpl
      sc.listenerBus, //LiveListenerBus 异步处理事件的对象,从sc中获取
      sc.env.mapOutputTracker.asInstanceOf[MapOutputTrackerMaster], 
//运行在Driver端管理shuffle map task的输出
      sc.env.blockManager.master, 
//运行在driver端的BlockManagerMaster,管理整个Job的Block信息,从sc中获取
      sc.env)  //sc.env就 是sparkEnv
  }

  def this(sc: SparkContext) = this(sc, sc.taskScheduler)
//给MetricsSystem使用的,DAGSchedulerSource测量的信息是stage.failedStages,
stage.runningStages,stage.waiting-Stage,stage.allJobs,stage.activeJobs
  private[spark] val metricsSource: DAGSchedulerSource = new DAGSchedulerSource(this)

  private[scheduler] val nextJobId = new AtomicInteger(0)// 生成JobId
  private[scheduler] def numTotalJobs: Int = nextJobId.get() // 总的Job数
  private val nextStageId = new AtomicInteger(0) // 下一个StageId
  // 记录某个job对应的包含的所有stage
  private[scheduler] val jobIdToStageIds = new HashMap[Int, HashSet[Int]]
// 记录StageId对应的Stage
private [scheduler] val stageIdToStage = new HashMap[ Int, Stage]
// 记录每一个shuffle对应的ShuffleMapStage,key为shuffleId

  private[scheduler] val shuffleToMapStage = new HashMap[Int, ShuffleMapStage] 

// 记录处于Active状态的job,key为jobId, value为ActiveJob类型对象
private[scheduler] val jobIdToActiveJob = new HashMap[Int, ActiveJob]

  // Stages we need to run whose parents aren't done
  // 等待运行的Stage,一般这些是在等待Parent Stage运行完成才能开始
  private[scheduler] val waitingStages = new HashSet[Stage]

  // Stages we are running right now
  // 处于Running状态的Stage
  private[scheduler] val runningStages = new HashSet[Stage]

  // Stages that must be resubmitted due to fetch failures
  // 失败原因为fetch failures的Stage,并等待重新提交
  private[scheduler] val failedStages = new HashSet[Stage]
  // active状态的Job列表
  private[scheduler] val activeJobs = new HashSet[ActiveJob]

  /**
   * Contains the locations that each RDD's partitions are cached on.  
This map's keys are RDD ids and its values are arrays indexed by partition numbers.
 Each array value is the set of locations where that RDD partition is cached.
   *
   * All accesses to this map should be guarded by synchronizing on it 
(see SPARK-4454).
    * 包含每个RDD分区缓存的位置。 map的key是RDD ID,其值是由分区号索引的数组。 
每个数组值都是该RDD分区缓存的位置组。 所有对map的访问都应该通过同步进行保护
   */
  private val cacheLocs = new HashMap[Int, IndexedSeq[Seq[TaskLocation]]]

  // For tracking failed nodes, we use the MapOutputTracker's epoch number, 
which is sent with every task. When we detect a node failing, 
we note the current epoch number and failed
  // executor, increment it for new tasks, and use this to ignore stray
 ShuffleMapTask results.
  // TODO: Garbage collect information about failure epochs when we know there
 are no more
  //       stray messages to detect.
  //为了跟踪失败的节点,我们使用MapOutputTracker的epoch编号,它随每个任务一起发送。 
当我们检测到一个节点失败时,
  // 我们注意到当前的epoch编号和失败的执行器,为新任务增加它,并用它来忽略杂散ShuffleMapTask
结果。
  private val failedEpoch = new HashMap[String, Long]
  /** 决定是否将task的输出放到hdfs中,该类会在dirver或executor上被实例化,
  * 在executor上请求提交输入将指向dirver的OutputCommitCoordinator,driver关
联OutputCommitCoordinatorEndpoint
  */
  private [scheduler] val outputCommitCoordinator = env.outputCommitCoordinator

  // A closure serializer that we reuse.
  // This is only safe because DAGScheduler runs in a single thread.
  //spark如果第一个要优化的地方,默认是javar,如果使用Kryo进行网终传输,
会比使用java提升10倍以上
  private val closureSerializer = SparkEnv.get.closureSerializer.newInstance()

  /** If enabled, FetchFailed will not cause stage retry, in order to surface 
the problem.
    * 如果启用,FetchFailed将不会导致stage重试 */
  private val disallowStageRetryForTest = sc.getConf.getBoolean("spark.test.noStageRetry", false)

  private val messageScheduler =
    ThreadUtils.newDaemonSingleThreadScheduledExecutor("dag-scheduler-message")
  // 处理Scheduler事件的对象,这个类初始化的最后一行,调用eventProcessLoop.start()
  private[scheduler] val eventProcessLoop = new DAGSchedulerEventProcessLoop(this)

//DAGScheduler初始化的时候,DAGScheduler执行有TaskSchedulerImpl引用,然后将自己用setDAGScheduler设置给TaskSchedulerImpl
 
taskScheduler.setDAGScheduler(this)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值