Spark1.3从创建到提交:10)任务提交源码分析

原创 2017年01月05日 10:26:48

接着上一节的提交最前stage和其任务集,先看下TaskScheduler.submitTaskst的源码,该方法具体由TaskScheduler的子类TaskSchedulerImpl实现

override def submitTasks(taskSet: TaskSet) {
	//只保留关心的代码....
	backend.reviveOffers()
}
这里的backend为SparkDeploySchedulerBackend(SchedulerBackend的实现类),但是SparkDeploySchedulerBackend没有方法reviveOffers的实现,其实现在其父类CoarseGrainedSchedulerBackend
override def reviveOffers() {
	driverActor ! ReviveOffers
}
其给DriverActor(DriverActor是CoarseGrainedSchedulerBackend的一个内部类)发送了一个ReviveOffers的消息,接下来去DriverActor类中的receiveWithLogging看下这个方法
case ReviveOffers => makeOffers()
再看下makeOffers这个方法
//Make fake resource offers on all executors
def makeOffers() {
  launchTasks(scheduler.resourceOffers(executorDataMap.map { case (id, executorData) =>
	new WorkerOffer(id, executorData.executorHost, executorData.freeCores)
  }.toSeq))
}
该方法启开始向executor分派任务,继续看下launchTasks
def launchTasks(tasks: Seq[Seq[TaskDescription]]) {
  for (task <- tasks.flatten) {
	//获得当前sparkEnv的序列化器
	val ser = SparkEnv.get.closureSerializer.newInstance()
	//序列化任务
	val serializedTask = ser.serialize(task)
	if (serializedTask.limit >= akkaFrameSize - AkkaUtils.reservedSizeBytes) {
	 //..which exceeds max allowed
	}
	else {
	  val executorData = executorDataMap(task.executorId)
	  executorData.freeCores -= scheduler.CPUS_PER_TASK
	  //给excutor发送序列化好的task
	  executorData.executorActor ! LaunchTask(new SerializableBuffer(serializedTask))
	}
  }
}

该方法把任务集中的任务序列化后依次发送给executor,下面去CoarseGrainedExecutorBackend类中的receiveWithLogging方法

//drive->executor启动计算任务
case LaunchTask(data) =>
  if (executor == null) {
	logError("Received LaunchTask command but executor was null")
	System.exit(1)
  } else {
	val ser = env.closureSerializer.newInstance()
	//反序列化接收到的task
	val taskDesc = ser.deserialize[TaskDescription](data.value)
	logInfo("Got assigned task " + taskDesc.taskId)
	//taskDesc.serializedTask是才是真正task的序列化内容
	executor.launchTask(this, taskId = taskDesc.taskId, attemptNumber = taskDesc.attemptNumber,
	  taskDesc.name, taskDesc.serializedTask)
 }

executor把接收到的task反序列化后,调用executor.launchTask进行处理,代码如下

def launchTask(
	context: ExecutorBackend,
	taskId: Long,
	attemptNumber: Int,
	taskName: String,
	serializedTask: ByteBuffer) {
	//使用TaskRunner封装task,TaskRunner实现了Runnable接口
	val tr = new TaskRunner(context, taskId = taskId, attemptNumber = attemptNumber, taskName,serializedTask)
	runningTasks.put(taskId, tr)
	//把task交给线程池执行
	threadPool.execute(tr)
}

接下来,实现了Runnable接口的TaskRunner.run方法会被调用

 override def run() {
      val deserializeStartTime = System.currentTimeMillis()
      Thread.currentThread.setContextClassLoader(replClassLoader)
      val ser = env.closureSerializer.newInstance()
      execBackend.statusUpdate(taskId, TaskState.RUNNING, EMPTY_BYTE_BUFFER)
      var taskStart: Long = 0
      startGCTime = gcTime
      try {
		//第1次反序列获获得taskFiles, taskJars, taskBytes
        val (taskFiles, taskJars, taskBytes) = Task.deserializeWithDependencies(serializedTask)
        updateDependencies(taskFiles, taskJars)
		//第2次序列化获得可以执行的task
        task = ser.deserialize[Task[Any]](taskBytes, Thread.currentThread.getContextClassLoader)
        taskStart = System.currentTimeMillis()
		//调用Task的run方法执行该task
        val value = task.run(taskAttemptId = taskId, attemptNumber = attemptNumber)
		//some other code.....
	  }
  }
上面的代码最终反序列化出了可以执行的task,并调用了Task.run方法进行执行,其代码如下
 final def run(taskAttemptId: Long, attemptNumber: Int): T = {
    context = new TaskContextImpl(stageId = stageId, partitionId = partitionId,
      taskAttemptId = taskAttemptId, attemptNumber = attemptNumber, runningLocally = false)
    TaskContextHelper.setTaskContext(context)
    context.taskMetrics.setHostname(Utils.localHostName())
    taskThread = Thread.currentThread()
    if (_killed) {
      kill(interruptThread = false)
    }
    try {
      runTask(context)
    } finally {
      context.markTaskCompleted()
      TaskContextHelper.unset()
    }
  }
该方法是一个不重写的方法,最终调用了该类中的 runTask(context)方法,runTask是一个抽象方法,其实现类有ResultTask和ShuffleMapTask,下面看ResultTask中的runTask方法
  override def runTask(context: TaskContext): U = {
    // Deserialize the RDD and the func using the broadcast variables.
    val ser = SparkEnv.get.closureSerializer.newInstance()
    val (rdd, func) = ser.deserialize[(RDD[T], (TaskContext, Iterator[T]) => U)](
      ByteBuffer.wrap(taskBinary.value), Thread.currentThread.getContextClassLoader)
	  
    metrics = Some(context.taskMetrics)
    func(context, rdd.iterator(partition, context))
  }
上面代码通过反序列化广播变量中的task获得了任务中的rdd和在该rdd上的操作func,然后使用func作用于该rdd获得新分区的数据,其具体原理可以参看之前的文章:Spark核心RDD:计算函数compute

版权声明:本文为博主原创文章,未经博主允许不得转载。

Spark1.3从创建到提交:7)SparkContext.runJob源码分析

以一个简单的WordCount为例,看下runJob的具体执行流程 object WordCount { def main(args: Array[String]) { val conf=...
  • jiangpeng59
  • jiangpeng59
  • 2017年01月04日 09:17
  • 224

Spark1.3从创建到提交:2)spark-submit和SparkContext源码分析

spark-sumit脚本 假如Spark提交的命令如下: bin/spark-submit --jar lib/spark-examples-1.3.1-hadoop2.6.0.jar --clas...
  • jiangpeng59
  • jiangpeng59
  • 2016年12月29日 21:32
  • 552

Spark1.3从创建到提交:3)任务调度初始化源码分析

上一节在SparkContext中也提及到了,在该类中创建了一个任务调度器,下面我们具体来分析这个方法 private[spark] var (schedulerBackend, taskSched...
  • jiangpeng59
  • jiangpeng59
  • 2016年12月30日 11:37
  • 315

Spark1.3从创建到提交:5)Executor启动源码分析

c
  • jiangpeng59
  • jiangpeng59
  • 2017年01月02日 09:20
  • 400

Spark1.3从创建到提交:4)资源分配源码分析

接着上一节Master里case RegisterApplication最后的一个资源调度方法schedule() ,假如我提交的任务需要6G,10Cores,而集群的部署如下所示: ...
  • jiangpeng59
  • jiangpeng59
  • 2016年12月30日 17:39
  • 439

Spark1.3从创建到提交:8)DAGScheduler.runJob源码分析

接着上一节,先看下类DAGScheduler的源码(只保留关心的) class DAGScheduler( private[scheduler] val sc: SparkContext,...
  • jiangpeng59
  • jiangpeng59
  • 2017年01月04日 20:56
  • 271

Hadoop2.*源码分析之Job任务提交与执行

写MapReduce程序创建一个Job执行时一般使用下面这个方法System.exit(job.waitForCompletion(true) ? 0 : 1);今天来分析以下Job是如何被执行的wa...
  • ltliyue
  • ltliyue
  • 2016年05月01日 23:28
  • 1024

Java Web提交任务到Spark Standalone集群并监控

Java Web提交任务到Spark Standalone集群并监控 环境 工程下载路径 Spark任务提交流程 问题及问题解决 后记Java Web提交任务到Spark Standalone集群并监...
  • fansy1990
  • fansy1990
  • 2017年11月16日 16:29
  • 714

Spark2.2任务提交运行机制及源码解析

源码版本:2.2 参考《Spark内核机制解析及性能调优》 如有错误请指正 一、Spark运行的核心概念 Spark调度器的设计体现得非常简洁清晰和高效,其输入是Spark RDD,输出是Sp...
  • junerli
  • junerli
  • 2017年10月19日 16:27
  • 241

Storm集群提交任务

本人原帖地址:http://www.jianshu.com/p/6783f1ec2da0,欢迎访问留言。 准备工作: 1. 将开发好的jar包上传到服务器package目录下 2. S...
  • gaodp1986
  • gaodp1986
  • 2015年12月30日 12:59
  • 256
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Spark1.3从创建到提交:10)任务提交源码分析
举报原因:
原因补充:

(最多只允许输入30个字)