SparkStreaming执行过程分析

本文深入剖析SparkStreaming的执行过程,包括启动流计算引擎、接收并存储数据以及生成Batch Job处理数据。详细解释了如何启动StreamingContext、JobScheduler,Receiver如何接收存储数据,以及Job的生成和提交。
摘要由CSDN通过智能技术生成

简介

SparkStreaming为每一个数据源启动对应的Reciver(接收器),接收器以任务的形式运行在应用的Executor进程中,从输入源接收数据,把数据分组为小的批次(batch),并保存为RDD,最后提交Spark Job执行。流计算执行过程流程图如下所示,主要包含三大步骤:

  1. 启动流计算引擎(启动JobScheduler和JobGenerator);
  2. 接收并存储数据(启动Receiver,接收数据,生成Block);
  3. 生成Batch Job处理数据(流数据转换为RDD,生成并提交Job);

SparkStreaming执行过程流程图
SparkStreaming执行过程示意图

下面以以spark streaming文档中创建Socket Stream的例子来描述任务的执行流程。

val lines = ssc.socketTextStream("localhost", 9999)
val words = lines.flatMap(_.split(" "))
val pairs = words.map(word => (word, 1))
val wordCounts = pairs.reduceByKey(_ + _)
wordCounts.print()

启动流计算引擎

流计算任务的主要入口是创建并启动StreamingContext对象,其中维护了任务配置信息、DStreamGraph(用来定义DStream,并管理他们的依赖关系)和JobScheduler(用来生成和调度job,其中维护了ReceiverTracker和JobGenerator实例)实例。

StreamingContext.start启动流计算

通过调用StreamingContext的start方法启动流计算,其主要包括以下几个过程:

  1. 校验启动StreamingContext的合法性;

  2. 启动JobScheduler任务调度器;

  3. 将当前StreamingContext对象设置为活跃单例对象;

  4. 在指标体系中注册流计算相关指标;

  5. 在Spark UI界面中添加StreamingTab页面;

     StreamingContext.scala
     /**
      * 启动流计算
      *
      * @throws IllegalStateException 如果StreamingContext已经被停止。
      */
     def start(): Unit = synchronized {	// 加同步锁,避免多个StreamingContext同时被启动
       state match {
         case INITIALIZED =>
           startSite.set(DStream.getCreationSite())
     	  // 该锁用于保护StreamingContext的激活以及在getActiveOrCreate()方法中对StreamingContext单例的访问
           StreamingContext.ACTIVATION_LOCK.synchronized {
             StreamingContext.assertNoOtherContextIsActive()
             try {
               validate()
     
               // 在新线程中启动流调度器,以便可以重置线程本地属性(如call sites和job groups),
     		  // 而不会影响当前线程中的这些属性值。
               ThreadUtils.runInNewThread("streaming-start") {
                 sparkContext.setCallSite(startSite.get)
                 sparkContext.clearJobGroup()
                 sparkContext.setLocalProperty(SparkContext.SPARK_JOB_INTERRUPT_ON_CANCEL, "false")
                 savedProperties.set(SerializationUtils.clone(sparkContext.localProperties.get()))
     			// 启动JobScheduler
                 scheduler.start()
               }
               state = StreamingContextState.ACTIVE
               scheduler.listenerBus.post(
                 StreamingListenerStreamingStarted(System.currentTimeMillis()))
             } catch {
               case NonFatal(e) =>
                 logError("Error starting the context, marking it as stopped", e)
                 scheduler.stop(false)
                 state = StreamingContextState.STOPPED
                 throw e
             }
             StreamingContext.setActiveContext(this)
           }
           logDebug("Adding shutdown hook") // force eager creation of logger
           shutdownHookRef = ShutdownHookManager.addShutdownHook(
             StreamingContext.SHUTDOWN_HOOK_PRIORITY)(stopOnShutdown)
           // 在StreamingContext开始时注册流指标
           assert(env.metricsSystem != null)
           env.metricsSystem.registerSource(streamingSource)
           uiTab.foreach(_.attach())
           logInfo("StreamingContext started")
         case ACTIVE =>
           logWarning("StreamingContext has already been started")
         case STOPPED =>
           throw new IllegalStateException("StreamingContext has already been stopped")
       }
     }
    

其中JobScheduler的启动是关键,下面我们重点来进行分析。

JobScheduler.start启动Job调度器

JobScheduler的启动由以下步骤组成:

  1. 创建EventLoop的匿名实现类,主要用于处理各类JobSchedulerEvent事件,如JobStarted和JobCompleted事件。

  2. 启动StreamingListenerBus,实现原理与LiveListenerBus相同,主要用于更新Spark UI中StreamTab的内容。

  3. 创建并启动ReceiverTracker,用于处理数据接收、数据缓存、Block生成等工作。

  4. 启动JobGenerator,负责对DstreamGraph的初始化、Dstream与RDD的转换、生成Job、提交执行等工作。

     // JobScheduler.scala
     def start(): Unit = synchronized {	// 加同步锁保证多线程安全,不会被多个线程同时调用启动
       if (eventLoop != null) return // 调度器已经被启动
     
       logDebug("Starting JobScheduler")
       eventLoop = new EventLoop[JobSchedulerEvent]("JobScheduler") {
     	// 处理JobStarted、JobCompleted事件
         override protected def onReceive(event: JobSchedulerEvent): Unit = processEvent(event)
     
         override protected def onError(e: Throwable): Unit = reportError("Error in job scheduler", e)
       }
       eventLoop.start()
     
       // 将各输入流的rate controllers监听器连接到接收batch完成的更新操作上
       for {
         inputDStream <- ssc.graph.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值