在《Spark源码分析之七:Task运行(一)》一文中,我们详细叙述了Task运行的整体流程,最终Task被传输到Executor上,启动一个对应的TaskRunner线程,并且在线程池中被调度执行。继而,我们对TaskRunner的run()方法进行了详细的分析,总结出了其内Task执行的三个主要步骤:
Step1:Task及其运行时需要的辅助对象构造,主要包括:
1、当前线程设置上下文类加载器;
2、获取序列化器ser;
3、更新任务状态TaskState;
4、计算垃圾回收时间;
5、反序列化得到Task运行的jar、文件、Task对象二进制数据;
6、反序列化Task对象二进制数据得到Task对象;
7、设置任务内存管理器;
Step2:Task运行:调用Task的run()方法,真正执行Task,并获得运行结果value
Step3:Task运行结果处理:
1、序列化Task运行结果value,得到valueBytes;
2、根据Task运行结果大小处理Task运行结果valueBytes:
2.1、如果Task运行结果大小大于所有Task运行结果的最大大小,序列化IndirectTaskResult,IndirectTaskResult为存储在Worker上BlockManager中DirectTaskResult的一个引用;
2.2、如果 Task运行结果大小超过Akka除去需要保留的字节外最大大小,则将结果写入BlockManager,Task运行结果比较小的话,直接返回,通过消息传递;
2.3、Task运行结果比较小的话,直接返回,通过消息传递。
大体流程大概就是如此。我们先回顾到这里。那么,接下来的问题是,任务内存管理器是什么?如何计算开始垃圾回收时间?Task的run()方法的执行流程是什么?IndirectTaskResult,或者BlockManager又是如何传递任务运行结果至应用程序即客户端的?
不要着急,我们一个一个来解决。
关于任务内存管理器TaskMemoryManager,可以参照《Spark源码分析之九:内存管理模型》一文,只要知道它是任务运行期间各区域内存的管理者就行,这里不再赘述。
接下来,我们重点分析下Task的run()方法,看看Task实际运行时的处理逻辑。其代码如下:
/**
* Called by [[Executor]] to run this task.
* 被Executor调用以执行Task
*
* @param taskAttemptId an identifier for this task attempt that is unique within a SparkContext.
* @param attemptNumber how many times this task has been attempted (0 for the first attempt)
* @return the result of the task along with updates of Accumulators.
*/
final def run(
taskAttemptId: Long,
attemptNumber: Int,
metricsSystem: MetricsSystem)
: (T, AccumulatorUpdates) = {
// 创建一个Task上下文实例:TaskContextImpl类型的context
context = new TaskContextImpl(
stageId,
partitionId,
taskAttemptId,
attemptNumber,
taskMemoryManager,
metricsSystem,
internalAccumulators,
runningLocally = false)
// 将context放入TaskContext的taskContext变量中
// taskContext变量为ThreadLocal[TaskContext]
TaskContext.setTaskContext(context)
// 设置主机名localHostName、内部累加器internalAccumulators等Metrics信息
context.taskMetrics.setHostname(Utils.localHostName())
context.taskMetrics.setAccumulatorsUpdater(context.collectInternalAccumulators)
// task线程为当前线程
taskThread = Thread.currentThread()
if (_killed) {// 如果需要杀死task,调用kill()方法,且调用的方式为不中断线程
kill(interruptThread = false)
}
try {
// 调用runTask()方法,传入Task上下文信息context,执行Task,并调用Task上下文的collectAccumulators()方法,收集累加器
(runTask(context), context.collectAccumulators())
} finally {
// 上下文标记Task完成
context.markTaskCompleted()
try {
Utils.tryLogNonFata