Spark源码解读之Job提交

Spark源码解读(3)——从集群启动到Job提交 1,Master启动 Master启动过程主要做了两件事: 1)启动一个守护线程定时对Worker的TimeOut进行Check,默认TimeOut时间为60s checkForWorkerTimeOutTask = forwardMessageThread.scheduleAtFixedRate(new Runnable { override def run(): Un 阅读详情

上一篇讲解了Spark源码解读之RDD构建和转换过程,上一篇的RDD操作都是transform,也就是说结果会是一个新的RDD,并不会进行真正地计算,真正会引发Spark计算操作时action。比如first、count、collect。

WordCounts.collect()会真正出发一个Job的执行。


调用流程:

org.apache.spark.SparkContext.runJob

org.apache.spark.scheduler.DAGScheduler.runJob

org.apache.spark.scheduler.DAGScheduler.submitJob

org.apache.spark.scheduler.DAGSchedulerEventProcessLoop.post

org.apache.spark.scheduler.DAGSchedulerEventProcessLoop.run

org.apache.spark.scheduler.DAGSchedulerEventProcessLoop.onReceive

org.apache.spark.scheduler.DAGSchedulerEventProcessLoop.doOnReceive

org.apache.spark.scheduler.DAGScheduler.handleJobSubmitted


RDD的collect源码如下:

  // 返回RDD所包含的所有元素
  def collect(): Array[T] = withScope {
    val results = sc.runJob(this, (iter: Iterator[T]) => iter.toArray)
    Array.concat(results: _*)
  }

内部直接调用SparkContext的runJob方法,SparkContext中的runJob方法进行了重载,这儿会从最开始的一个runJob方法调用到最后的一个runJob方法,源码如下:

  /**
  * rdd为最后转换后生成的ShuffledRDD
  * func为(ctx: TaskContext, it: Iterator[T]) => cleanedFunc(it),cleanedFunc为val cleanedFunc = clean(func)
  * partitions为0 until rdd.partitions.length
  * resultHandler为(index, res) => results(index) = res,results为val results = new Array[U](partitions.size)
  */
  def runJob[T, U: ClassTag](
      rdd: RDD[T],
      func: (TaskContext, Iterator[T]) => U,
      partitions: Seq[Int], 
      resultHandler: (Int, U) => Unit): Unit = {
    if (stopped.get()) {
      throw new IllegalStateException("SparkContext has been shutdown")
    }
    val callSite = getCallSite
    val cleanedFunc = clean(func)
    logInfo("Starting job: " + callSite.shortForm)
    if (conf.getBoolean("spark.logLineage", false)) {
      logInfo("RDD's recursive dependencies:\n" + rdd.toDebugString)
    }
    // 调用dagScheduler的runJob方法
    dagScheduler.runJob(rdd, cleanedFunc, partitions, callSite, resultHandler, localProperties.get)
    progressBar.foreach(_.finishAll())
    // 对RDD进行checkpoint
    rdd.doCheckpoint()
  }

DAGScheduler中的runJob方法,该方法中通过submitJob方法进行任务的提交,具体源码如下:

  def runJob[T, U](
      rdd: RDD[T],
      func: (TaskContext, Iterator[T]) => U,
      partitions: Seq[Int],
      callSite: CallSite,
      resultHandler: (Int, U) => Unit,
      properties: Properties): Unit = {
    val start = System.nanoTime
    // 调用DAGScheduler中的submitJob方法,返回JobWaiter对象,该对象等待job完成,完成后调用resultHandler函数进行后续处理
    val waiter = submitJob(rdd, func, partitions, callSite, resultHandler, properties)
    waiter.awaitResult() match {
    	// 成功
      case JobSucceeded =>
        logInfo("Job %d finished: %s, took %f s".format
          (waiter.jobId, callSite.shortForm, (System.nanoTime - start) / 1e9))
      // 失败
      case JobFailed(exception: Exception) =>
        logInfo("Job %d failed: %s, took %f s".format
          (waiter.jobId, callSite.shortForm, (System.nanoTime - start) / 1e9))
        // SPARK-8644: Include user stack trace in exceptions coming from DAGScheduler.
        val callerStackTrace = Thread.currentThread().getStackTrace.tail
        exception.setStackTrace(exception.getStackTrace ++ callerStackTrace)
        throw exception
    }
  }

submitJob方法源码如下:

  def submitJob[T, U](
      rdd: RDD[T],
      func: (TaskContext, Iterator[T]) => U,
      partitions: Seq[Int],
      callSite: CallSite,
      resultHandler: (Int, U) => Unit,
      properties: Properties): JobWaiter[U] = {
    // Check to make sure we are not launching a task on a partition that does not exist.
    // 检查任务,确保所执行的任务所在的partition存在
    val maxPartitions = rdd.partitions.length
    partitions.find(p => p >= maxPartitions || p < 0).foreach { p =>
      throw new IllegalArgumentException(
        "Attempting to access a non-existent partition: " + p + ". " +
          "Total number of partitions: " + maxPartitions)
    }
		
		// 唯一的Job Id
    val jobId = nextJobId.getAndIncrement()
    if (partitions.size == 0) {
      // Return immediately if the job is running 0 tasks
      return new JobWaiter[U](this, jobId, 0, resultHandler)
    }

    assert(partitions.size > 0)
    val func2 = func.asInstanceOf[(TaskContext, Iterator[_]) => _]
    // 创建JobWaiter,该JobWaiter会被阻塞,直到Job完成或取消
    val waiter = new JobWaiter(this, jobId, partitions.size, resultHandler)
    // eventProcessLoop是DAGSchedulerEventProcessLoop类的实例,调用post方法提交JobSubmitted到event队列,eventThread后台进程会对该任务进行提交处理
    eventProcessLoop.post(JobSubmitted(
      jobId, rdd, func2, partitions.toArray, callSite, waiter,
      SerializationUtils.clone(properties)))
    waiter
  }

DAGSchedulerEventProcessLoop继承EventLoop,先来看下EventLoop类源码:

/**
 * An event loop to receive events from the caller and process all events in the event thread. It
 * will start an exclusive event thread to process all events.
 *
 * Note: The event queue will grow indefinitely. So subclasses should make sure `onReceive` can
 * handle events in time to avoid the potential OOM.
 */
private[spark] abstract class EventLoop[E](name: String) extends Logging {

  private val eventQueue: BlockingQueue[E] = new LinkedBlockingDeque[E]()

  private val stopped = new AtomicBoolean(false)

  private val eventThread = new Thread(name) {
    setDaemon(true)

    override def run(): Unit = {
      try {
        while (!stopped.get) {
          val event = eventQueue.take()
          try {
            onReceive(event) // 抽象方法,由子类去实现
          } catch {
            case NonFatal(e) => {
              try {
                onError(e)
              } catch {
                case NonFatal(e) => logError("Unexpected error in " + name, e)
              }
            }
          }
        }
      } catch {
        case ie: InterruptedException => // exit even if eventQueue is not empty
        case NonFatal(e) => logError("Unexpected error in " + name, e)
      }
    }

  }

  // 省略部分方法
  
  /**
   * Put the event into the event queue. The event thread will process it later.
   */
  def post(event: E): Unit = {
    eventQueue.put(event)
  }

  /**
   * Invoked in the event thread when polling events from the event queue.
   *
   * Note: Should avoid calling blocking actions in `onReceive`, or the event thread will be blocked
   * and cannot process events in time. If you want to call some blocking actions, run them in
   * another thread.
   */
  protected def onReceive(event: E): Unit
  
}

从EventLoop类的源码可以看出来EventLoop中包含了一个eventThread的后台线程,该线程的作用是从队列中拿到任务,然后对其调用onReceive方法。此外,post方法是将事件提交到队列中。由于onReceive是一个抽象方法,具体实现需要看其子类,下面是DAGSchedulerEventProcessLoop的部分源码:

private[scheduler] class DAGSchedulerEventProcessLoop(dagScheduler: DAGScheduler)
  extends EventLoop[DAGSchedulerEvent]("dag-scheduler-event-loop") with Logging {

  private[this] val timer = dagScheduler.metricsSource.messageProcessingTimer

  /**
   * The main event loop of the DAG scheduler.
   * 在使用post方法提交后,eventThread进程会将调用该方法进行处理
   */
  override def onReceive(event: DAGSchedulerEvent): Unit = {
    val timerContext = timer.time()
    try {
      doOnReceive(event)
    } finally {
      timerContext.stop()
    }
  }
	
  private def doOnReceive(event: DAGSchedulerEvent): Unit = event match {
    case JobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties) =>
      dagScheduler.handleJobSubmitted(jobId, rdd, func, partitions, callSite, listener, properties)

    case MapStageSubmitted(jobId, dependency, callSite, listener, properties) =>
      dagScheduler.handleMapStageSubmitted(jobId, dependency, callSite, listener, properties)

    case StageCancelled(stageId) =>
      dagScheduler.handleStageCancellation(stageId)

    case JobCancelled(jobId) =>
      dagScheduler.handleJobCancellation(jobId)

    case JobGroupCancelled(groupId) =>
      dagScheduler.handleJobGroupCancelled(groupId)

    case AllJobsCancelled =>
      dagScheduler.doCancelAllJobs()

    case ExecutorAdded(execId, host) =>
      dagScheduler.handleExecutorAdded(execId, host)

    case ExecutorLost(execId) =>
      dagScheduler.handleExecutorLost(execId, fetchFailed = false)

    case BeginEvent(task, taskInfo) =>
      dagScheduler.handleBeginEvent(task, taskInfo)

    case GettingResultEvent(taskInfo) =>
      dagScheduler.handleGetTaskResult(taskInfo)

    case completion @ CompletionEvent(task, reason, _, _, taskInfo, taskMetrics) =>
      dagScheduler.handleTaskCompletion(completion)

    case TaskSetFailed(taskSet, reason, exception) =>
      dagScheduler.handleTaskSetFailed(taskSet, reason, exception)

    case ResubmitFailedStages =>
      dagScheduler.resubmitFailedStages()
  }

}

从上面的分析可以看出来,Spark Job的提交最后会由名为“dag-scheduler-event-loop”的后台进程去处理,然后调用dagScheduler.handleJobSubmitted完成Job的最终提交,之后下一节将讲解Job如何划分Stage。

Spark Streaming源码解读之Job详解 一:Spark Streaming Job生成深度思考 1. 做大数据例如Hadoop,Spark等,如果不是流处理的话,一般会有定时任务。例如10分钟触发一次,1个小时触发一次,这就是做流处理的感觉,一切不是流处理,或者与流处理无关的数据都将是没有价值的数据,以前做批处理的时候其实也是隐形的在做流处理。 2. JobGenerator构造的时候有一个核心的参数是jobScheduler, 阅读详情

相关推荐

Spark Streaming源码解读之Job动态生成和深度思考

本博文主要包含以下内容:1、 Spark Streaming Job 生成深度思考 2 、Spark Streaming Job 生成源码解析一 :Spark Streaming Job 生成深度思考输入的DStream有很多来源Kafka、Socket、Flume,输出的DStream其实是逻辑级别的Action,是Spark Streaming框架提出的,其底层翻译成为物理级别的Action,

吾心光明 2315

Spark源码分析之DAGScheduler以及stage的划分

Spark源码分析之DAGScheduler以及stage的划分

happy19870612's blog 1078

Spark RDD算子源码解读

结合spark1.5.0的RDD源码API及自己一年以来的开发经验,介绍Spark RDD算子的功能、原理及调用方式。

tanglizhe1105的博客 3640

Spark的Sate划分源码剖析

Spark的Sate划分源码剖析 1.主程序Driver program 需要初始化SparkContext 2.在初始化时会创建三个重量级的类 ​ 1.创建schedulerBacked 负责连接后端计算资源 ​ 2.创建taskScheduler 负责阶段任务调度 ​ 3.创建DAGScheduler 负责阶段划分 // Create and start the scheduler //初始...

Java学习笔记 416

Cocos Creator 2.4 APK逆向实战:如何完整提取游戏资源并还原工程(附工具包)

本文详细介绍了针对Cocos Creator 2.4打包的APK进行逆向工程的完整流程,包括环境搭建、资源索引解析、打包文件提取、图集拆分以及工程结构还原。重点讲解了如何利用config.json和.jsb文件完整提取游戏资源,并生成可被Cocos Creator识别的工程目录与.meta文件,最终实现工程还原的目标。

h3i4j的博客 1493

Windows下用RKDevTool解包update.img的完整流程(含常见错误排查)

本文详细介绍了在Windows系统下使用RKDevTool工具解包update.img固件的完整流程。从工具准备、环境搭建到逐步解包操作,重点解析了输出文件结构,并提供了修改分区后重新打包的实战方法。文章还针对解包失败、打包错误等常见问题提供了详细的排查与解决方案,旨在帮助嵌入式开发者和爱好者高效完成固件分析与定制工作。

aa123的博客 715

spark wordcount

1、这是idea下+spark包的源码 package main.scala import org.apache.spark.{SparkConf,SparkContext} /** * Created by root on 1/12/17. */ object WordCount { val conf =new SparkConf() val sc = new Sp

AStar的博客 1723

实战ByteTrack:用C++手把手教你实现多目标跟踪(附完整代码解析)

本文通过C++实战教程,手把手教你实现ByteTrack多目标跟踪算法。文章详细解析了算法核心,包括利用卡尔曼滤波进行状态预测、匈牙利算法完成数据关联,以及ByteTrack独特的三次匹配策略来有效处理低置信度检测框,并提供了完整的项目架构、代码实现与性能优化指南。

yolo5detector的博客 299

kotlin coroutine源码解析之Job取消以及异常的处理流程分析

异常的传导链,从子Job向父Job传导:如果父Job是supervisorJob的话,将不做处理,需要子Job自己处理;如果父Job是JobSupport的话,异常还会继续向父Job的父Job传递,直到根部Job。所以捕获异常的话在根ScopeCoroutine里面设置就比较合适,对于有supervisorJob的情况,需要在supervisorJob的子Job中设置异常处理器,supervisorJob自己不会处理异常。

u012345683的博客 2138

头歌平台-人工智能导论实验(人脸识别)

答案仅供参考,自己动手丰衣足食!!!

qq_52119136的博客 3026

深入解析西门子S7通信协议:从抓包到实战应用

本文深入解析西门子S7通信协议,从环境搭建、抓包分析到实战应用,提供了一套完整的协议分析方法。通过Wireshark工具进行抓包分析,详细拆解了S7协议各层结构,并结合真实案例,展示了如何利用抓包技术解决通信故障、优化性能及进行安全审计,是掌握工业通信底层原理的实用指南。

weixin_29224529的博客 281

【原】Spark中Job的提交源码解读

版权声明:本文为原创文章,未经允许不得转载。 Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码解读(一)http://www.cnblogs.com/yourarebest/p/5326678.html 1.Spark中Job的提交 以一个简单的runjob为例,源码如下: def r...

weixin_30443813的博客 139

CLASSB类移植(满足IEC 60730-1和60355-1标准)

针对软件认证需要满足IEC 60730-1和60335-1标准,ST公司专门提供的软件扩展包X-CUBE-CLASSB,它包含自测库和文档,支持多种STM32系列芯片,通过预集成测试库和标准化流程,帮助开发者快速满足国际安全标准,适用于家电、工业控制等高可靠性场景。ClassB软件包程序检测内容分为两个主要部分:启动时的自检和运行时的周期自检,MCU在上电的时候会进行复位启动,在启动完成以后会进行运行自检。

m0_61973119的博客 1401

Spark源码解读-JOB的提交与执行

本文以wordCount为例,详细说明spark创建和运行job的过程,重点是在进程及线程的创建。 实验环境搭建 在进行后续操作前,确保下列条件已满足。 1. 下载spark binary 0.9.1 2. 安装scala 3. 安装sbt 4. 安装java 启动spark-shell单机模式运行,即local模式 local模式运行非常简单,只要

sunlei1980的专栏 948

codex出现Reconnecting和stream disconnected before completion:stream closed before response.complete解决方案

本文主要介绍了在使用codex过程中出现Reconnecting和stream disconnected before completion: stream closed before response.completed的解决方案,希望能对使用codex的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 9万+

图像处理基础 || (一) python 读取图像的常用方式

图像的读取与保存是图像处理或计算机视觉领域中最基本的操作,python中有众多的库支持图像的读取、显示与存储,常用的库包括 matplotlib、skimage 和 Pillow 库。

weixin_40234309的博客 5683

hadoop源码分析_Spark2.x精通:Job触发流程源码深度剖析(一)

, 一、概述之前几篇文章对Spark集群的Master、Worker启动流程进行了源码剖析,后面直接从客户端角度出发,讲解了spark-submit任务提交过程及driver的启动;集群启动、任务提交、SparkContext初始化等前期准备工作完成之后,后面就是我们的主函数的代码Job如何触发的,本篇文章还是结合源码进行剖析。软件版本:sp...

weixin_39603613的博客 167

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Spark源码走读4——Scheduler

用户提交的Job到DAGScheduler后,会封装成ActiveJob,同时启动JobWaiter监听作业的完成情况。同时依据job中RDD的dependency和dependency属性(NarrowDependency,ShufflerDependecy),DAGScheduler会根据依赖关系的先后产生出不同的stage DAG(result stage, shuffle map ...

weixin_30902251的博客 211

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Spark 定制版:006~Spark Streaming源码解读之Job动态生成和深度思考

本讲内容:a. Spark Streaming Job生成深度思考 b. Spark Streaming Job生成源码解析注:本讲内容基于Spark 1.6.1版本(在2016年5月来说是Spark最新版本)讲解。上节回顾上节课,主要是从Spark Streaming+Spark SQL来实现分类最热门商品的在线动态计算的事例代码开始,并通过Spark源代码给大家贯通Spark Streami

zisheng_wang_DATA的博客 2769

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spark源码解读之Stage划分和提交

上一篇讲解了Spark源码解读之Job提交,这一篇主要讲解Stage划分和提交。 调用流程: org.apache.spark.scheduler.DAGScheduler.handleJobSubmitted org.apache.spark.scheduler.DAGScheduler.submitStage org.apache.spark.scheduler.DAGSche

Oner.wv的专栏 1604

5. Spark job提交流程源代码分析

本文主要分析了job提交的流程,主要包括stage划分与Task提交的流程。

cl2010abc的专栏 367

Spark源码分析-应用程序到底是如何提交到Spark并运行的?

网上有不少关于Spark应用程序提交流程分析的文章,有的鞭辟入里、有的浅尝辄止。但由于Spark代码版本更迭或关注点的缘故,总有一些自己想知道的细节不能系统的获取。所以打算基于spark-2.4.4(3.0-release版本还未发版),记录下自己对Spark应用程序提交和运行流程源码的一些分析和理解,同时通过打断点代码调试的方式,将完整的代码执行流程分享给大家,希望能做到真正的手把手带你读Spa...

merrily01的博客 299

Spark提交流程源码分析

说明 ①此文主要以Yarn Cluster模式为例说明提交流程 ②spark版本3.0 提交流程简图 提交流程描述 ①在YARN Cluster模式下,任务提交后会创建yarn客户端yarnClient,通过客户端和ResourceManager通讯申请启动ApplicationMaster ②随后ResourceManager分配container,在合适的NodeManager上启动ApplicationMaster。 ③ApplicationMaster主线程会启动一个子线程,命名为driver,然

qq1277419839的博客 358

【读懂面经中的源码】SPARK源码解析——Spark任务提交、调度、执行过程

【读懂面经中的源码】SPARK源码解析——Spark任务提交、调度、执行过程。这是读源码的博客,主要从面经出发,深入理解Spark任务提交、调度、执行过程,期间通过源码加深理解面经中提到的原理。文章包括三部分,分别是面筋部分、Spark任务提交、SparkContext初始化、Executor启动、Task启动的部分。文章三部分的讲解将有助于读者同学理解Spark任务提交、调度、执行详细原理,希望能够帮助到各位读者同学!!!

番茄薯仔 1126

Spark Streaming源码解读之Receiver在Driver详解

一:Receiver启动的方式设想 1. Spark Streaming通过Receiver持续不断的从外部数据源接收数据,并把数据汇报给Driver端,由此每个Batch Durations就可以根据汇报的数据生成不同的Job。 2. Receiver属于Spark Streaming应用程序启动阶段,那么我们找Receiver在哪里启动就应该去找Spark Streaming的启动。

snail_gesture的博客 1万+
上一篇: Spark源码解读之RDD构建和转换过程
下一篇: Spark源码解读之Stage划分和提交
Wang_AI
博客等级 码龄12年 988粉丝 162原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值