Task是介于DAGScheduler和TaskScheduler中间的接口
在DAGScheduler, 需要把DAG中的每个stage的每个partitions封装成task
最终把taskset提交给TaskScheduler

 

/**
* A task to execute on a worker node.
*/
private[spark] abstract class Task[T](val stageId: Int) extends Serializable {
def run(attemptId: Long): T //Task的核心函数
def preferredLocations: Seq[TaskLocation] = Nil //Spark关注locality,可以选择该task运行的location
var epoch: Long = -1 // Map output tracker epoch. Will be set by TaskScheduler.
var metrics: Option[TaskMetrics] = None
}

 

TaskContext

用于记录TaskMetrics和在Task中用到的callback

比如对于HadoopRDD, task完成时需要close input stream

package org.apache.spark
class TaskContext(
val stageId: Int,
val splitId: Int,
val attemptId: Long,
val runningLocally: Boolean = false,
val taskMetrics: TaskMetrics = TaskMetrics.empty() //TaskMetrics封装了task执行时一些指标和数据
) extends Serializable { @transient val onCompleteCallbacks = new ArrayBuffer[() => Unit] // Add a callback function to be executed on task completion. An example use
// is for HadoopRDD to register a callback to close the input stream.
def addOnCompleteCallback(f: () => Unit) {
onCompleteCallbacks += f
} def executeOnCompleteCallbacks() {
onCompleteCallbacks.foreach{_()}
}
}

 

ResultTask

对应于Result Stage直接产生结果

package org.apache.spark.scheduler
private[spark] class ResultTask[T, U](
stageId: Int,
var rdd: RDD[T],
var func: (TaskContext, Iterator[T]) => U,
var partition: Int,
@transient locs: Seq[TaskLocation],
var outputId: Int)
extends Task[U](stageId) with Externalizable { override def run(attemptId: Long): U = { // 对于resultTask, run就是返回执行的结果, 比如count值
val context = new TaskContext(stageId, partition, attemptId, runningLocally = false)
metrics = Some(context.taskMetrics)
try {
func(context, rdd.iterator(split, context)) // 直接就是对RDD的iterator调用func, 比如count函数
} finally {
context.executeOnCompleteCallbacks()
}
}
}

 

ShuffleMapTask

对应于ShuffleMap Stage, 产生的结果作为其他stage的输入

package org.apache.spark.scheduler
private[spark] class ShuffleMapTask(
stageId: Int,
var rdd: RDD[_],
var dep: ShuffleDependency[_,_],
var partition: Int,
@transient private var locs: Seq[TaskLocation])
extends Task[MapStatus](stageId)
with Externalizable
with Logging { override def run(attemptId: Long): MapStatus = {
val numOutputSplits = dep.partitioner.numPartitions // 从ShuffleDependency的partitioner中获取到shuffle目标partition的个数 val taskContext = new TaskContext(stageId, partition, attemptId, runningLocally = false)
metrics = Some(taskContext.taskMetrics) val blockManager = SparkEnv.get.blockManager // shuffle需要借助blockManager来完成
var shuffle: ShuffleBlocks = null
var buckets: ShuffleWriterGroup = null try {
// Obtain all the block writers for shuffle blocks.
val ser = SparkEnv.get.serializerManager.get(dep.serializerClass)
shuffle = blockManager.shuffleBlockManager.forShuffle(dep.shuffleId, numOutputSplits, ser) // 创建shuffleBlockManager, 参数是shuffleId和目标partitions数目
buckets = shuffle.acquireWriters(partition) // 生成shuffle目标buckets(对应于partition) // Write the map output to its associated buckets.
for (elem <- rdd.iterator(split, taskContext)) { // 从RDD中取出每个elem数据
val pair = elem.asInstanceOf[Product2[Any, Any]]
val bucketId = dep.partitioner.getPartition(pair._1) // 根据pair的key进行shuffle, 得到目标bucketid
buckets.writers(bucketId).write(pair) // 将pair数据写入bucket
}
      // Commit这些buckets到block, 其他的RDD会从通过shuffleid找到这些block, 并读取数据
// Commit the writes. Get the size of each bucket block (total block size).
var totalBytes = 0L
val compressedSizes: Array[Byte] = buckets.writers.map { writer: BlockObjectWriter => // 计算所有buckets写入文件data的size总和(压缩值)
writer.commit()
writer.close()
val size = writer.size()
totalBytes += size
MapOutputTracker.compressSize(size)
} // Update shuffle metrics.
val shuffleMetrics = new ShuffleWriteMetrics
shuffleMetrics.shuffleBytesWritten = totalBytes
metrics.get.shuffleWriteMetrics = Some(shuffleMetrics) return new MapStatus(blockManager.blockManagerId, compressedSizes) // 返回值为MapStatus, 包含blockManagerId和写入的data size, 会被注册到MapOutputTracker
} catch { case e: Exception =>
// If there is an exception from running the task, revert the partial writes
// and throw the exception upstream to Spark.
if (buckets != null) {
buckets.writers.foreach(_.revertPartialWrites())
}
throw e
} finally {
// Release the writers back to the shuffle block manager.
if (shuffle != null && buckets != null) {
shuffle.releaseWriters(buckets)
}
// Execute the callbacks on task completion.
taskContext.executeOnCompleteCallbacks()
}
}

 

TaskSet

用于封装一个stage的所有的tasks, 以提交给TaskScheduler

package org.apache.spark.scheduler
/**
* A set of tasks submitted together to the low-level TaskScheduler, usually representing
* missing partitions of a particular stage.
*/
private[spark] class TaskSet(
val tasks: Array[Task[_]],
val stageId: Int,
val attempt: Int,
val priority: Int,
val properties: Properties) {
val id: String = stageId + "." + attempt override def toString: String = "TaskSet " + id
}

Spark 源码分析 -- Task的更多相关文章

  1. Spark 源码分析 -- task实际执行过程

    Spark源码分析 – SparkContext 中的例子, 只分析到sc.runJob 那么最终是怎么执行的? 通过DAGScheduler切分成Stage, 封装成taskset, 提交给Task ...

  2. Spark源码分析 – 汇总索引

    http://jerryshao.me/categories.html#architecture-ref http://blog.csdn.net/pelick/article/details/172 ...

  3. Spark源码分析 – DAGScheduler

    DAGScheduler的架构其实非常简单, 1. eventQueue, 所有需要DAGScheduler处理的事情都需要往eventQueue中发送event 2. eventLoop Threa ...

  4. Spark源码分析之八:Task运行(二)

    在<Spark源码分析之七:Task运行(一)>一文中,我们详细叙述了Task运行的整体流程,最终Task被传输到Executor上,启动一个对应的TaskRunner线程,并且在线程池中 ...

  5. Spark源码分析之七:Task运行(一)

    在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在T ...

  6. Spark源码分析之六:Task调度(二)

    话说在<Spark源码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这个方法针对接收到的ReviveOffer ...

  7. Spark源码分析之五:Task调度(一)

    在前四篇博文中,我们分析了Job提交运行总流程的第一阶段Stage划分与提交,它又被细化为三个分阶段: 1.Job的调度模型与运行反馈: 2.Stage划分: 3.Stage提交:对应TaskSet的 ...

  8. spark 源码分析之二十一 -- Task的执行流程

    引言 在上两篇文章 spark 源码分析之十九 -- DAG的生成和Stage的划分 和 spark 源码分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的划分以及St ...

  9. spark 源码分析之二十二-- Task的内存管理

    问题的提出 本篇文章将回答如下问题: 1.  spark任务在执行的时候,其内存是如何管理的? 2. 堆内内存的寻址是如何设计的?是如何避免由于JVM的GC的存在引起的内存地址变化的?其内部的内存缓存 ...

随机推荐

  1. CSS3 Flex布局(伸缩布局盒模型)学习

    CSS3 Flex布局(伸缩布局盒模型)学习 转自:http://www.xifengxx.com/web-front-end/1408.html CSS2定义了四种布局:块布局.行内布局.表格布局盒 ...

  2. (七十二)自己定义通知NSNotification实现消息传递

    众所周知,iOS中一般在类之间传递消息使用较多的是delegate和block,另一种是基于通知进行的消息传递,我们经常是使用系统的通知.来实现一些功能.比如利用键盘尺寸改变的通知,我们能够依据键盘的 ...

  3. RDD缓存学习

    首先实现rdd缓存 准备了500M的数据 10份,每份 100万条,存在hdfs 中通过sc.textFile方法读取 val rdd1 = sc.textFile("hdfs://mini ...

  4. OpenERP 7 picking order 继承需要注意的地方

    stock.picking.out  和 stock.picking.in 都是继承自stock.picking 新添加columns时需要注意,在stock.picking.out和stock.pi ...

  5. oracle查询数据库最大连接数等信息

    .当前的数据库连接数 select count(*) from v$process where program='ORACLE.EXE(SHAD)'; .数据库允许的最大连接数 select valu ...

  6. 编译Spark2.1.2源码

    源码编译的shell脚本为 /dev/make-distribution.sh ,下载源码包解压就能找到.不同版本使用的参数有差异.可以直接查看make-distribution.sh文件. 下载sp ...

  7. android monkey app乱点测试

    Monkey是Android中的一个命令行工具 查看包名:查看电脑中某一位置的apk文件的包名:PC打开CMD-进入TMG目录-运行设备--查看包名aapt dump badging *.apk(ap ...

  8. go hmac使用

    https://github.com/danharper/hmac-examples 94 func generateSign(data, key []byte) string { 95 mac := ...

  9. 二分 + 模拟 - Carries

    Carries Problem's Link Mean: 给你n个数,让你计算这n个数两两组合相加的和进位的次数. analyse: 脑洞题. 首先要知道:对于两个数的第k位相加会进位的条件是:a%( ...

  10. 《javascript征途》学习笔记

    基础 1. 只有函数有作用域 2. 如果在<script src>的src 中设置了src特性,则script元素包含的任意代码就无效了.应该分开放到不同的script块中. 3. 外部j ...