Spark DAG 概述 及 Submit Job

DAG 概述

DAG,有向无环图,Directed Acyclic Graph的缩写,常用于建模。Spark中使用DAG对RDD的关系进行建模,描述了RDD的依赖关系,这种关系也被称之为lineage,RDD的依赖关系使用Dependency维护,参考Spark RDD之Dependency,DAG在Spark中的对应的实现为DAGScheduler

介绍DAGScheduler中的一些概念,有助于理解后续流程。

名词 解释
Job 调用RDD的一个action,如count,即触发一个Job,spark中对应实现为ActiveJob,DAGScheduler中使用集合activeJobs和jobIdToActiveJob维护Job
Stage 代表一个Job的DAG,会在发生shuffle处被切分,切分后每一个部分即为一个Stage,Stage实现分为ShuffleMapStage和ResultStage,一个Job切分的结果是0个或多个ShuffleMapStage加一个ResultStage
Task 最终被发送到Executor执行的任务,和stage的ShuffleMapStage和ResultStage对应,其实现分为ShuffleMapTask和ResultTask

Submit Job

在介绍 Submit Job 前,需要先引入一个概念:EventLoop

EventLoop

DAGScheduler 使用 EventLoop(LinkedBlockingDeque)异步处理Job的流程,程序由同步改为异步是优化并发,提升性能的常见手段,在spark中使用的非常多。

异步处理借助于 EventLoop 实现,EventLoop内部维护了LinkedBlockingDeque,LinkedBlockingDeque是基于链表实现的双端阻塞队列,定义如下:

private[spark] abstract class EventLoop[E](name: String) extends Logging {

  private val eventQueue: BlockingQueue[E] = new LinkedBlockingDeque[E]()

  private val stopped = new AtomicBoolean(false)

  private val eventThread = new Thread(name) {
    override def run(): Unit = {
      while (!stopped.get) {
          val event = eventQueue.take()
          onReceive(event)
      }
    }
  }
  
  //抽象方法,需要在子类实现
  protected def onReceive(event: E): Unit
  
  ...  
}

DAGSchedulerEvent

DAGScheduler对事件进行了分类。
事件的父类为 DAGSchedulerEvent,也是EventLoop中存储的类型。

具体子类类型为:


Spark DAG 概述 及 Submit Job_第1张图片

上面的事件基本能够见名知义,下面 Submit Job 部分会涉及到第一个事件JobSubmitted。

DAGSchedulerEventProcessLoop

EventLoop的实现类,主要为抽象方法onReceive的实现,处理各种不同DAGSchedulerEvent。如下:
DAGSchedulerEventProcessLoop:

private[scheduler] class DAGSchedulerEventProcessLoop(dagScheduler: DAGScheduler)
  extends EventLoop[DAGSchedulerEvent]("dag-scheduler-event-loop") with Logging {

  override def onReceive(event: DAGSchedulerEvent): Unit = {
    doOnReceive(event)
  }
}

DAGSchedulerEventProcessLoop 继承自 EventLoop,并重写了 onReceive 方法,看下 doOnReceive:

  private def doOnReceive(event: DAGSchedulerEvent): Unit = event match {
    case JobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties) =>
      dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties)

    case MapStageSubmitted(jobId, dependency, callSite, listener, properties) =>
      dagScheduler.handleMapStageSubmitted(jobId, dependency, callSite, listener, properties)
    ...
  }

onReceive方法调用doOnReceive,doOnReceive中根据事件的类型,调用DAGScheduler的不同方法处理。如上面提到的JobSubmitted事件,交给DAGScheduler的handleJobSubmitted方法处理。

SubmitJob

Spark 任务调度之Driver send Task 我们介绍了SparkContextrunJob方法调用DAGSchedulerrunJob方法,把RDD交给DAGScheduler处理。

查看DAGScheduler 中 runJob方法中的部分代码:


submitJob 方法部分代码:

eventProcessLoop.post(JobSubmitted(
      jobId, rdd, func2, partitions.toArray, callSite, waiter,
      SerializationUtils.clone(properties)))

调用 DAGSchedulerEventProcessLoop 的 post方法,post是在父类 EventLoop 实现:

  def post(event: E): Unit = {
    eventQueue.put(event)
  }

把事件添加到队列 eventQueue,上面代码中提到,EventLoop 中创建了线程,并在线程中 只要while (!stopped.get),就会从队列中取event:val event = eventQueue.take(),并回调 onReceive(event),最终在 DAGSchedulerEventProcessLoop 中消费事件。

总结

介绍了EventLoop的概念及DAGScheduler使用EventLoop异步处理Job的流程,但是EventLoop中维护的LinkedBlockingDeque并没有指定容量,默认容量为Integer.MAX_VALUE,如果eventThread消费不及时,有OOM的风险,最后 DAGScheduler 消费 JobSubmitted 事件的流程大致如下

Spark DAG 概述 及 Submit Job_第2张图片

  1. ①-④流程,提交 JobSubmitted 事件到 LinkedBlockingDeque。
  2. (1)-(4)流程,eventThread循环消费 LinkedBlockingDeque,最终将JobSubmitted事件交给 DAGScheduler 的 handleJobSubmitted 方法处理。

你可能感兴趣的:(Spark DAG 概述 及 Submit Job)