概述
前面我们分析了DAG中相关的基础变量以及事件处理器,我们知道RDD是Spark操作的基本数据结构,RDD提供了两种类型的操作:transformation
和action
。transformation
采用的懒策略,如果只是将transformation
提交是不会执行计算的,计算只有在action
被提交的时候才被触发,只有在action
的方法中才会调用org.apache.spark.SparkContext#runJob
方法来提交Job,本节我们来分析下Job提交以及Stage的生成&提交过程。
触发Job生成
我们先来看下Job是如何触发以及提交的,首先我们知道action算子才会执行真正的计算,是划分job的标识,所以当我们触发action算子时候,就会触发job,然后进行提交,我们来看下count
这个action算子是如何得到Job以及提交Job的。我们来看下count算子,主要是通过sparkContext的runJob来进行Job创建,提供了count计算需要的funciton:
def count(): Long = sc.runJob(this, Utils.getIteratorSize _).sum
我们跟踪下SparkContext里面runJob实现,会经过几个runJob的转换,添加partition,结果处理函数,进行一些清理工作之后,最终在该方法中将提交请求转发到了dagScheduler
。
def runJob[T, U: ClassTag](rdd: RDD[T], func: Iterator[T] => U): Array[U] = {
runJob(rdd, func, 0 until rdd.partitions.length)
}
def runJob[T, U: ClassTag](rdd: RDD[T], func: Iterator[T] => U,
partitions: Seq[Int]): Array[U] = {
val cleanedFunc = clean(func)
runJob(rdd, (ctx: TaskContext, it: Iterator[T]) => cleanedFunc(it), partitions)
}
// resultHandler是将结果填充result数组,返回数组结果信息
def runJob[T, U: ClassTag](rdd: RDD[T], func: (TaskContext, Iterator[T]) => U,
partitions: Seq[Int]): Array[U] = {
val results = new Array[U](partitions.size)
runJob[T, U](rdd, func, partitions, (index, res) => results(index) = res)
results
}
def runJob[T, U: ClassTag](rdd: RDD[T], func: (TaskContext, Iterator[T]) => U,
partitions: Seq[Int], resultHandler: (Int, U) => Unit): Unit = {
val callSite = getCallSite
// 对传入给算子的操作进行清理检查
val cleanedFunc = clean(func)
// 将DAG及RDD提交给DAGScheduler进行调度
dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get)
progressBar.foreach(_.finishAll())
rdd.doCheckpoint() // 保存检查点
}
DagScheduler
里面的runJob是真正在给定RDD上运行一个Job,结果交给resultHandler
处理,主要是将各个结果的数据填充结果Array,返回给Driver,该函数会通过submitJob
来提交job,并进行等待结果处理完成。
def runJob[T, U]( rdd: RDD[T], func: (TaskContext, Iterator[T]) => U,
partitions: Seq[Int], callSite: CallSite, resultHandler: (Int, U) => Unit,
properties: Properties): Unit = {
// 启动时间
val start = System.nanoTime
// 提交Job,该方法是异步的,会立即返回JobWaiter对象
val waiter = submitJob(rdd, func, partitions, callSite, resultHandler, properties)
// 提交Job,该方法是异步的,会立即返回JobWaiter对象
val awaitPermission = null.asInstanceOf[scala.concurrent.CanAwait]
// 等待Job处理完毕
waiter.completionFuture.ready(Duration.Inf)(awaitPermission)
// 获取运行结果
waiter.completionFuture.value.get match {
case scala.util.Success(_) => // Job执行成功
logInfo("Job %d finished: %s, took %f s".format
(waiter