原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/)  



Spark streaming 程序的运行过程是将DStream的操作转化成RDD的操作,Spark Streaming 和 Spark Core 的关系如下图(图片来自spark官网)
Spark Streaming 会按照程序设定的时间间隔不断动态生成Job来处理输入数据,这里的Job生成是指将Spark Streaming 的程序翻译成Spark内核的RDD操作,翻译的过程并不会触发Job的运行,Spark Streaming 会将翻译的处理逻辑封装在Job对象中,最后会将Job提交到集群上运行。这就是Spark Streaming 运行的基本过程。下面详细介绍Job动态生成和提交过程。

首先,当SparkStreaming的start方法调用后,整个Spark Streaming 程序开始运行,按照指定的时间间隔生成Job并提交给集群运行,在生成Job的工程中主要核心对象有
    1.JobScheduler  
    2.JobGenerator
    3.DStreamGraph
    4.DStream
其中, JobScheduler 负责启动JobGenerator生成Job,并提交生成的Job到集群运行,这里的Job不是在spark core 中提到的job,它只是作业运行的代码模板,是逻辑级别的,可以类比java线程中的Runnable接口实现,不是真正运行的作业, 它封装了由DStream转化而来的RDD操作.JobGenerator负责定时调用DStreamingGraph的generateJob方法生成Job和清理Dstream的元数据, DStreamGraph持有构成DStream图的所有DStream对象,并调用DStream的generateJob方法生成具体Job对象.DStream生成最终的Job交给JobScheduler 调度执行。整体过程如下图所示:



原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/)

下面结合源码分析每一步过程 (源码中黄色背景部分为核心逻辑代码,例如 : scheduler.start() ) :
首先,StreamingContext起动时调用start方法
    1. try {
    1. validate()
    1. // Start the streaming scheduler in a new thread, so that thread local properties
    1. // like call sites and job groups can be reset without affecting those of the
    1. // current thread.
    1. ThreadUtils.runInNewThread("streaming-start") {
    1. sparkContext.setCallSite(startSite.get)
    1. sparkContext.clearJobGroup()
    1. sparkContext.setLocalProperty(SparkContext.SPARK_JOB_INTERRUPT_ON_CANCEL, "false")
    1. savedProperties.set(SerializationUtils.clone(
    1. sparkContext.localProperties.get()).asInstanceOf[Properties])
    1. scheduler.start()
    1. }
    1. state = StreamingContextState.ACTIVE
    1. } catch {
    1. case NonFatal(e) =>
    1. logError("Error starting the context, marking it as stopped", e)
    1. scheduler.stop(false)
    1. state = StreamingContextState.STOPPED
    1. throw e
    1. }

其中调用了scheduler的start方法,此处的scheduler 就是 org.apache.spark.streaming.scheduler.JobScheduler 对象,
StreamingContext持有org.apache.spark.streaming.scheduler.JobScheduler对象的引用。
下面看一下JobScheduler的start方法:

    1. eventLoop = new EventLoop[JobSchedulerEvent]("JobScheduler") {
    1. override protected def onReceive(event: JobSchedulerEvent): Unit = processEvent(event)
    1. override protected def onError(e: Throwable): Unit = reportError("Error in job scheduler", e)
    1. }
    1. eventLoop.start()
    1. // attach rate controllers of input streams to receive batch completion updates
    1. for {
    1. inputDStream <- ssc.graph.getInputStreams
    1. rateController <- inputDStream.rateController
    1. } ssc.addStreamingListener(rateController)
    1. listenerBus.start()
    1. receiverTracker = new ReceiverTracker(ssc)
    1. inputInfoTracker = new InputInfoTracker(ssc)
    1. executorAllocationManager = ExecutorAllocationManager.createIfEnabled(
    1. ssc.sparkContext,
    1. receiverTracker,
    1. ssc.conf,
    1. ssc.graph.batchDuration.milliseconds,
    1. clock)
    1. executorAllocationManager.foreach(ssc.addStreamingListener)
    1. receiverTracker.start()
    1. jobGenerator.start()
    1. executorAllocationManager.foreach(_.start())
    1. logInfo("Started JobScheduler")

可以看到JobScheduler调用了jobGeneratorstart方法和eventLoop的start方法,eventLoop用来接收JobSchedulerEvent消息,并交给processEvent函数进行处理
代码如下:
    1. private def processEvent(event: JobSchedulerEvent) {
    1. try {
    1. event match {
    1. case JobStarted(job, startTime) => handleJobStart(job, startTime)
    1. case JobCompleted(job, completedTime) => handleJobCompletion(job, completedTime)
    1. case ErrorReported(m, e) => handleError(m, e)
    1. }
    1. } catch {
    1. case e: Throwable =>
    1. reportError("Error in job scheduler", e)
    1. }
    1. }

 可以看到JobScheduler中的eventLoop只处理JobStarted,JobCompleted和ErrorReported 三类消息,这三类消息的处理不是Job动态生成的核心逻辑代码先略过,(注意:后面JobGenerator中也有个eventLoop不要和这里的eventLoop混淆。)
JobGenerator的start方法首先new了一个EventLoop对象eventLoop,并复写onReceive(),将收到的JobGeneratorEvent 消息交给 processEvent 方法处理.源码如下:

    1. /** Start generation of jobs */
    1. def start(): Unit = synchronized {
    1. if (eventLoop != null) return // generator has already been started
    1. // Call checkpointWriter here to initialize it before eventLoop uses it to avoid a deadlock.
    1. // See SPARK-10125
    1. checkpointWriter
    1. eventLoop = new EventLoop[JobGeneratorEvent]("JobGenerator") {
    1. override protected def onReceive(event: JobGeneratorEvent): Unit = processEvent(event)
    1. override protected def onError(e: Throwable): Unit = {
    1. jobScheduler.reportError("Error in job generator", e)
    1. }
    1. }
    1. eventLoop.start()
    1. if (ssc.isCheckpointPresent) {
    1. restart()
    1. } else {
    1. startFirstTime()
    1. }
    1. }
JobGenerator创建了eventLoop对象之后调用该对象的start方法,启动监听进程,准备接收JobGeneratorEvent类型消息交给processEvent函数处理,然后调用了startFirstTime方法,该方法启动DStreamGraph和定时器,定时器启动后根据程序设定的时间间隔给eventLoop对象发送GenerateJobs消息,如下图:


原创文章,转载请注明:转载自 周岳飞博客(http://zhou-yuefei.iteye.com/)


eventLoop对象收到 GenerateJobs 消息交个processEvent方法处理,processEvent收到该消息,调用generateJobs方法处理,源码如下:

    1. /** Generate jobs and perform checkpoint for the given `time`. */
    1. private def generateJobs(time: Time) {
    1. // Checkpoint all RDDs marked for checkpointing to ensure their lineages are
    1. // truncated periodically. Otherwise, we may run into stack overflows (SPARK-6847).
    1. ssc.sparkContext.setLocalProperty(RDD.CHECKPOINT_ALL_MARKED_ANCESTORS, "true")
    1. Try {
    1. jobScheduler.receiverTracker.allocateBlocksToBatch(time) // allocate received blocks to batch
    1. graph.generateJobs(time) // generate jobs using allocated block
    1. } match {
    1. case Success(jobs) =>
    1. val streamIdToInputInfos = jobScheduler.inputInfoTracker.getInfo(time)
    1. jobScheduler.submitJobSet(JobSet(time, jobs, streamIdToInputInfos))
    1. case Failure(e) =>
    1. jobScheduler.reportError("Error generating jobs for time " + time, e)
    1. }
    1. eventLoop.post(DoCheckpoint(time, clearCheckpointDataLater = false))
    1. }

JobGenerator中的generateJobs方法主要关注两行代码,首先调用graph的generateJobs方法,给方法返回Success(jobs) 或者 Failure(e),其中的jobs就是该方法返回的Job对象集合,如果Job创建成功,再调用JobScheduler的submitJobSet方法将job提交给集群执行。
首先分析Job对象的产生,DStreamGraph 的start方法源码:
    1. def generateJobs(time: Time): Seq[Job] = {
    1. logDebug("Generating jobs for time " + time)
    1. val jobs = this.synchronized {
    1. outputStreams.flatMap { outputStream =>
    1. val jobOption = outputStream.generateJob(time)
    1. jobOption.foreach(_.setCallSite(outputStream.creationSite))
    1. jobOption
    1. }
    1. }
    1. logDebug("Generated " + jobs.length + " jobs for time " + time)
    1. jobs
    1. }

DStreamGraph 的start方法源码调用了outputStream对象的generateJob方法,ForeachDStream重写了该方法:
ForeachDStream的generateJob 将用户编写的DStream处理函数封装在jobFunc中,并将其传入Job对象,至此Job的生成。
接下来分析Job提交过程,JobScheduler负责Job的提交,核心代码在submitJobSet方法中:

    1. def submitJobSet(jobSet: JobSet) {
    1. if (jobSet.jobs.isEmpty) {
    1. logInfo("No jobs added for time " + jobSet.time)
    1. } else {
    1. listenerBus.post(StreamingListenerBatchSubmitted(jobSet.toBatchInfo))
    1. jobSets.put(jobSet.time, jobSet)
    1. jobSet.jobs.foreach(job => jobExecutor.execute(new JobHandler(job)))
    1. logInfo("Added jobs for time " + jobSet.time)
    1. }
    1. }

其中jobExecutor对象是一个线程池,JobHandler实现了Runnable接口,在JobHandler 的run方法中会调用传入的job对象的run方法。

疑问:Job的run方法执行是如何触发RDD的Action操作从而出发job的真正运行的呢?我们下次再具体分析,请随时关注博客更新!

原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/)









6.Spark streaming技术内幕 : Job动态生成原理与源码解析的更多相关文章

  1. Spark streaming技术内幕6 : Job动态生成原理与源码解析

    原创文章,转载请注明:转载自 周岳飞博客(http://www.cnblogs.com/zhouyf/)  Spark streaming 程序的运行过程是将DStream的操作转化成RDD的操作,S ...

  2. Spark技术内幕: Task向Executor提交的源码解析

    在上文<Spark技术内幕:Stage划分及提交源码分析>中,我们分析了Stage的生成和提交.但是Stage的提交,只是DAGScheduler完成了对DAG的划分,生成了一个计算拓扑, ...

  3. 7.spark Streaming 技术内幕 : 从DSteam到RDD全过程解析

    原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/)   上篇博客讨论了Spark Streaming 程序动态生成Job的过程,并留下一个疑问: ...

  4. Spark技术内幕:Stage划分及提交源码分析

    http://blog.csdn.net/anzhsoft/article/details/39859463 当触发一个RDD的action后,以count为例,调用关系如下: org.apache. ...

  5. 9. Spark Streaming技术内幕 : Receiver在Driver的精妙实现全生命周期彻底研究和思考

        原创文章,转载请注明:转载自 听风居士博客(http://www.cnblogs.com/zhouyf/)       Spark streaming 程序需要不断接收新数据,然后进行业务逻辑 ...

  6. JDK1.8 动态代理机制及源码解析

    动态代理 a) jdk 动态代理 Proxy, 核心思想:通过实现被代理类的所有接口,生成一个字节码文件后构造一个代理对象,通过持有反射构造被代理类的一个实例,再通过invoke反射调用被代理类实例的 ...

  7. Spark集群任务提交流程----2.1.0源码解析

    Spark的应用程序是通过spark-submit提交到Spark集群上运行的,那么spark-submit到底提交了什么,集群是怎样调度运行的,下面一一详解. 0. spark-submit提交任务 ...

  8. 贯通Spark Streaming JobScheduler内幕实现和深入思考

    本节主要内容: 一.SparkStreaming Job生成深度思考 二.SparkStreaming Job生成源码解析 JobScheduler的地位非常的重要,所有的关键都在JobSchedul ...

  9. Spark Streaming运行流程及源码解析(一)

    本系列主要描述Spark Streaming的运行流程,然后对每个流程的源码分别进行解析 之前总听同事说Spark源码有多么棒,咱也不知道,就是疯狂点头.今天也来撸一下Spark源码. 对Spark的 ...

随机推荐

  1. Bigbluebutton服务执行过程及相关配置文件

    BigBlueButton服务列表 BigBlueButton由许多开源的服务组成,看似很麻烦,实际上拆分开每一个服务就很简单了,组件化平台化.究竟BBB都用到了哪些开源服务?我们来列举一下,名称均带 ...

  2. PowerDesigner逆向生成

    人越长大话越少,我们不再说今天受了委屈,不再说谁谁谁不理我了我好难过,不再分享生活中的琐事. 我知道人和人之间没法互相理解,大家都很忙,针也没扎在别人身上. 所以把那些还没说出口的话消化在每一步走过的 ...

  3. 确保web安全的https、确认访问用户身份的认证(第七章、第八章)

    第七章 确保web安全的https 1.http的缺点: (1)通信使用明文,内容可能会被窃听 (2)不验证通信方的身份,因此有可能遭遇伪装 (3)无法证明报文的完整性,因此有可能已遭篡改. 2.通信 ...

  4. C11简洁之道:循环的改善

    1.  for循环的新用法 在C++98/03中,通过for循环对一个容器进行遍历,一般有两种方法,常规的for循环,或者使用<algorithm>中的for_each方法. for循环遍 ...

  5. 解决配置JAVA_HOME JDK版本不变的问题

    解决方案:修改环境变量Path 因为PATH环境变量中默认将system32等系统重要目录添加在最前面,所以运行java -version时当然是调用system32目录下的java.exe了. 所以 ...

  6. Item 9 覆盖equals时总要覆盖hashCode

    为什么覆盖equals时,总要覆盖hashCode?   原因是,根据Object规范: 如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCod ...

  7. 自定义View的实现流程

    1.继承View组件,比如,LabelView继承了View   2.重写两个构造方法,比如,对于自定义View LabelView   LabelView(Context context),如果该自 ...

  8. Bzoj3224 / Tyvj 1728 普通替罪羊树

    Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 12015  Solved: 5136 Description 您需要写一种数据结构(可参考题目标题), ...

  9. Spring 框架的设计理念与设计模式分析(山东数漫江湖)

    Spring 的骨骼架构 Spring 总共有十几个组件,但是真正核心的组件只有几个,下面是 Spring 框架的总体架构图: 图 1 .Spring 框架的总体架构图 从上图中可以看出 Spring ...

  10. 爬虫--BeautifulSoup

    什么是BeautifulSoup? BeautifulSoup支持的一些解析库 基本使用 from bs4 import BeautifulSoup html =""" ...