Spark 源码分析 -- Task
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的更多相关文章
- Spark 源码分析 -- task实际执行过程
Spark源码分析 – SparkContext 中的例子, 只分析到sc.runJob 那么最终是怎么执行的? 通过DAGScheduler切分成Stage, 封装成taskset, 提交给Task ...
- Spark源码分析 – 汇总索引
http://jerryshao.me/categories.html#architecture-ref http://blog.csdn.net/pelick/article/details/172 ...
- Spark源码分析 – DAGScheduler
DAGScheduler的架构其实非常简单, 1. eventQueue, 所有需要DAGScheduler处理的事情都需要往eventQueue中发送event 2. eventLoop Threa ...
- Spark源码分析之八:Task运行(二)
在<Spark源码分析之七:Task运行(一)>一文中,我们详细叙述了Task运行的整体流程,最终Task被传输到Executor上,启动一个对应的TaskRunner线程,并且在线程池中 ...
- Spark源码分析之七:Task运行(一)
在Task调度相关的两篇文章<Spark源码分析之五:Task调度(一)>与<Spark源码分析之六:Task调度(二)>中,我们大致了解了Task调度相关的主要逻辑,并且在T ...
- Spark源码分析之六:Task调度(二)
话说在<Spark源码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这个方法针对接收到的ReviveOffer ...
- Spark源码分析之五:Task调度(一)
在前四篇博文中,我们分析了Job提交运行总流程的第一阶段Stage划分与提交,它又被细化为三个分阶段: 1.Job的调度模型与运行反馈: 2.Stage划分: 3.Stage提交:对应TaskSet的 ...
- spark 源码分析之二十一 -- Task的执行流程
引言 在上两篇文章 spark 源码分析之十九 -- DAG的生成和Stage的划分 和 spark 源码分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的划分以及St ...
- spark 源码分析之二十二-- Task的内存管理
问题的提出 本篇文章将回答如下问题: 1. spark任务在执行的时候,其内存是如何管理的? 2. 堆内内存的寻址是如何设计的?是如何避免由于JVM的GC的存在引起的内存地址变化的?其内部的内存缓存 ...
随机推荐
- Python|PyCharm安装scrapy包
[转]http://www.cnblogs.com/xiaoli2018/p/4566639.html
- MacBook Air 2014 安装win7
1.准备一个4G以上容量USB3.0 U盘.制作一个带USB3.0驱动的win7 2.将制作好的win7iso镜像文件复制到macbook上,插上U盘,运行Boot Camp助理: 3.选择默认勾选项 ...
- TOTP:Time-based One-time Password Algorithm(基于时间的一次性密码算法)
TOTP:Time-based One-time Password Algorithm(基于时间的一次性密码算法) TOTP - Time-based One-time Password Algori ...
- python学习笔记(8)--爬虫下载占位图片
说明: 1. 虽然很简单,但总忘,所以还是记下来吧! 2. http://placekitten.com/300/300这个画猫的网站老是打开没有图片,改成了http://placehold.it/3 ...
- MyBatis增删改查模板
1. 首先,和Spring整合一下 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=& ...
- Hdu 2236 无题II 最大匹配+二分
题目链接: pid=2236">Hdu 2236 解题思路: 将行和列理解为二分图两边的端点,给出的矩阵即为二分图中的全部边, 假设二分图能全然匹配,则说明 不同行 不同列的n个元素 ...
- LVM简介
3. 创建VG.. 7 4. 创建LV.. 9 5.LV格式化及挂载... 10 一.LVM简介 LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauel ...
- kickstart安装步骤
1.1 环境说明 [root@test ~]# cat /etc/redhat-release CentOS release 6.9 (Final) [root@test ~]# uname -r 2 ...
- js获取textaera对象(object)的值
for(i in pstrWord ){ alert(i); //获得属性 alert(pstrWord[i]); //获得属性值 } 1.js输出object对象方法如下: function wri ...
- php接入域账号登陆代码
php接入域账号登陆代码 //替换本地登录为AD域用户认证//edit by ZhangJin on 2015-05-23 -START-$dn = $user_account.'@fun ...