spark源码(十三)excutor启动task原理剖析

文章目录


前言

excutor反向注册,worker中为application启动的 executor,实际上是启动了这个 CoargeGrainedExecutorBackend进程,

/**
    * 创建DriverEndpoint和DriverEndpointRef
    * */
  override def start() {
    val properties = new ArrayBuffer[(String, String)]
    for ((key, value) <- scheduler.sc.conf.getAll) {
      if (key.startsWith("spark.")) {
        properties += ((key, value))
      }
    }

    // TODO (prashant) send conf instead of properties
    // 创建driver的引用
    driverEndpoint = createDriverEndpointRef(properties)
  }

  // 创建driver的引用
  protected def createDriverEndpointRef(
      properties: ArrayBuffer[(String, String)]): RpcEndpointRef = {
    rpcEnv.setupEndpoint(ENDPOINT_NAME, createDriverEndpoint(properties))
  }
  // Launch tasks returned by a set of resource offers
    /**
      * launchTasks的处理步骤如下:
      *   1.序列化TaskDescription
      *   2.去除TaskDescription所描述任务分配的ExecutorData信息,并且将ExecutorData描述的空闲CPU核数减去任务所占用的核数。
      *   3.向Executor所在的CoarseGrainedExecutorBackend进程中的CoarseGrainedExecutorBackend发送LaunchTask消息。
      *
      *   在该方法中,会将当前task在driver上序列化后发送到executor上运行。序列化后的task大小,如果超过128MB-200KB,当前
      *   task不能执行,并且把task对应的taskSetManager设置成zombie模式,因此其中剩余的task都不再执行。如果不超过该限制,
      *   则会把task分发到executor上。
      *
      *   序列化后的Task发送到Executor上执行,是通过Akka来进行的。其实上面那个限制大小也是Akka决定的。在AkkaUtils类中可以
      *   看到,这个限制由两个参数来确定spark.akka.frameSize,默认值为128,经过处理后,最终计算成128MB,表示Akka最大能传
      *   递的消息大小。除了用于发送序列化后的task数据之外,Akka本身会使用到一部分空间存储一些额外的数据,这一部分的大小为200KB
      *   。所以在默认情况下,对Akka来说,一个消息最大能传递的数据大小为128MB - 200KB。这两个参数在后面Executor中也会用到。
      */
    private def launchTasks(tasks: Seq[Seq[TaskDescription]]) {
      for (task <- tasks.flatten) {
        // 序列化当前task
        val serializedTask = TaskDescription.encode(task)
        // 如果当前task序列化后的大小超过了128MB-200KB,跳过当前task,并把对应的taskSetManager置为zombie模式
        if (serializedTask.limit >= maxRpcMessageSize) {
          scheduler.taskIdToTaskSetManager.get(task.taskId).foreach { taskSetMgr =>
            try {
              var msg = "Serialized task %s:%d was %d bytes, which exceeds max allowed: " +
                "spark.rpc.message.maxSize (%d bytes). Consider increasing " +
                "spark.rpc.message.maxSize or using broadcast variables for large values."
              msg = msg.format(task.taskId, task.index, serializedTask.limit, maxRpcMessageSize)
              taskSetMgr.abort(msg)
            } catch {
              case e: Exception => logError("Exception in error callback", e)
            }
          }
        }
        // 序列化后的task大小不超过限制时,将当前task发送到Executor上执行。
        else {
          // 获取当前task分配到的executor相关信息
          val executorData = executorDataMap(task.executorId)
          executorData.freeCores -= scheduler.CPUS_PER_TASK

          logDebug(s"Launching task ${task.taskId} on executor id: ${task.executorId} hostname: " +
            s"${executorData.executorHost}.")

          /**
            * 发送运行task的消息,由org.apache.spark.executor.CoarseGrainedExecutorBackend.receive()方法处理
            */
          executorData.executorEndpoint.send(LaunchTask(new SerializableBuffer(serializedTask)))
        }
      }
    }
  /**
    * 调用Executor的launchTask方法时,标志着任务执行阶段的开始。执行过程如下:
    *
    * 1.创建TaskRunner,并且将其与taskId,taskName及serializedTask(这三个都被TaskDescription对象包含了)
    *   添加到runningTasks = new ConcurrentHashMap[Long,TaskRuuner]中。
    * 2.TaskRunner实现了Runnable接口(Scala中成为继承Runnable特质),最后使用线程池执行TaskRunner.
    *
    */
  def launchTask(context: ExecutorBackend, taskDescription: TaskDescription): Unit = {
    //实例化一个TaskRunner对象来执行Task
    val tr = new TaskRunner(context, taskDescription)
    //将Task加入到正在运行的Task队列
    runningTasks.put(taskDescription.taskId, tr)

    /**
      * 任务最终运行的地方
      * 在worker的线程池中,
      * 线程池调用org.apache.spark.executor.Executor.TaskRunner.run()方法运行任务
      * 就是这个文件中的内部类
      */
    threadPool.execute(tr)
  }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值