Spark定制班第16课:Spark Streaming源码解读之数据清理内幕彻底解密

55 篇文章 0 订阅
55 篇文章 0 订阅
这一课讲数据清理的内容。
Spark Streaming应用是持续不断地运行着的。如果不对内存资源进行有效管理,内存就有可能很快就耗尽。
Spark Streaming应用一定有自己的对象、数据、元数据的清理机制。
如果把Spark Streaming研究透彻了,那也就能驾驭Spark的各种应用程序。

本课主要内容:
1 Spark Streaming数据清理原因和现象
2 Spark Streaming数据清理代码解析
 
Spark Streaming应用中的对象、数据、元数据,是我们操作DStream时产生的。

DStream:

  private[streaming] var  generatedRDDs  = new HashMap[Time, RDD[T]] ()

DStream根据时间生成的RDD是放入了这个generatedRDDs中。

DStream的持久化:

  /** Persist RDDs of this DStream with the default storage level (MEMORY_ONLY_SER) */
  def persist(): DStream[T] = persist(StorageLevel.MEMORY_ONLY_SER)

  /** Persist RDDs of this DStream with the default storage level (MEMORY_ONLY_SER) */
  def cache(): DStream[T] = persist()

对DStream的cache就是对RDD的cache。

RDD产生、释放也应跟时钟有关的。JobGenerator:

  private val timer = new RecurringTimer( clock , ssc.graph.batchDuration.milliseconds,
    longTime => eventLoop.post( GenerateJobs (new Time(longTime))), "JobGenerator")

这个可以不断的发出事件。

清理处理是怎么开始的呢?
先给出数据清理的总流程图:


JobScheduler的JobHandler会在需要时发出 JobCompleted 的消息。
JobScheduler. JobHandler.run:
...
        if (_eventLoop != null) {
          _eventLoop.post(JobStarted(job, clock.getTimeMillis()))
          // Disable checks for existing output directories in jobs launched by the streaming
          // scheduler, since we may need to write output to an existing directory during checkpoint
          // recovery; see SPARK-4835 for more details.
          PairRDDFunctions.disableOutputSpecValidation.withValue(true) {
            job.run()
          }
          _eventLoop = eventLoop
          if (_eventLoop != null) {
            _eventLoop.post( JobCompleted (job, clock.getTimeMillis()))
          }
        } else {
          // JobScheduler has been stopped.
        }
...

JobScheduler. processEvent:

  private def processEvent(event: JobSchedulerEvent) {
    try {
      event match {
        case JobStarted(job, startTime) => handleJobStart(job, startTime)
        case  JobCompleted (job, completedTime) =>  handleJobCompletion (job, completedTime)
        case ErrorReported(m, e) => handleError(m, e)
      }
    } catch {
      case e: Throwable =>
        reportError("Error in job scheduler", e)
    }
  }

JobCompleted事件的处理,是调用了handleJobCompletion。
JobScheduler. handleJobCompletion:

  private def handleJobCompletion(job: Job, completedTime: Long) {
    val jobSet = jobSets.get(job.time)
    jobSet.handleJobCompletion(job)
    job.setEndTime(completedTime)
    listenerBus.post(StreamingListenerOutputOperationCompleted(job.toOutputOperationInfo))
    logInfo("Finished job " + job.id + " from job set of time " + jobSet.time)
    if (jobSet.hasCompleted) {
      jobSets. remove (jobSet.time)
      jobGenerator. onBatchCompletion (jobSet.time)
      logInfo("Total delay: %.3f s for time %s (execution: %.3f s)".format(
        jobSet.totalDelay / 1000.0, jobSet.time.toString,
        jobSet.processingDelay / 1000.0
      ))
      listenerBus.post(StreamingListenerBatchCompleted(jobSet.toBatchInfo))
    }
    job.result match {
      case Failure(e) =>
        reportError("Error running job " + job, e)
      case _ =>
    }
  }

JobSet做了清理,还调用了jobGenerator.onBatchCompletion。
JobGenerator.onBatchCompletion:

  /**
   * Callback called when a batch has been completely processed.
   */
  def onBatchCompletion(time: Time) {
    eventLoop.post( ClearMetadata (time))
  }

ClearMetadata消息和前面的GenerateJobs消息一样,也是在JobGenerator.processEvent里做处理。
JobGenerator.processEvent:

  /** Processes all events */
  private def processEvent(event: JobGeneratorEvent) {
    logDebug("Got event " + event)
    event match {
      case  GenerateJobs (time) => generateJobs(time)
      case  ClearMetadata (time) =>  clearMetadata (time)
      case DoCheckpoint(time, clearCheckpointDataLater) =>
        doCheckpoint(time, clearCheckpointDataLater)
      case ClearCheckpointData(time) => clearCheckpointData(time)
    }
  }

其中也有清理元数据事件(ClearMetadata)对应的处理。
JobGenerator. clearMetadata:

  /** Clear DStream metadata for the given `time`. */
  private def clearMetadata(time: Time) {
    ssc.graph. clearMetadata (time)

    // If checkpointing is enabled, then checkpoint,
    // else mark batch to be fully processed
    if (shouldCheckpoint) {
      eventLoop.post(DoCheckpoint(time, clearCheckpointDataLater = true))
    } else {
      // If checkpointing is not enabled, then delete metadata information about
      // received blocks (block data not saved in any case). Otherwise, wait for
      // checkpointing of this batch to complete.
      val maxRememberDuration = graph.getMaxInputStreamRememberDuration()
      jobScheduler.receiverTracker. cleanupOldBlocksAndBatches (time - maxRememberDuration)
      jobScheduler.inputInfoTracker. cleanup (time - maxRememberDuration)
      markBatchFullyProcessed(time)
    }
  }

可以看到有多项清理工作。
DStreamGraph.clearMetadata:

  def clearMetadata(time: Time) {
    logDebug("Clearing metadata for time " + time)
    this.synchronized {
      outputStreams.foreach(_. clearMetadata (time))
    }
    logDebug("Cleared old metadata for time " + time)
  }

其中清理了ForeachDStream。
DStream.clearMetadata:

  /**
   * Clear metadata that are older than `rememberDuration` of this DStream.
   * This is an internal method that should not be called directly. This default
   * implementation clears the old generated RDDs. Subclasses of DStream may override
   * this to clear their own metadata along with the generated RDDs.
   */
  private[streaming] def clearMetadata(time: Time) {
    val unpersistData = ssc.conf.getBoolean(" spark.streaming.unpersist ", true)
    val oldRDDs = generatedRDDs.filter(_._1 <= (time -  rememberDuration ))
    logDebug("Clearing references to old RDDs: [" +
      oldRDDs.map(x => s"${x._1} -> ${x._2.id}").mkString(", ") + "]")
    generatedRDDs --= oldRDDs.keys
    if (unpersistData) {
      logDebug("Unpersisting old RDDs: " + oldRDDs.values.map(_.id).mkString(", "))
      oldRDDs.values.foreach { rdd =>
        rdd.unpersist(false)
        // Explicitly remove blocks of BlockRDD
        rdd match {
          case b: BlockRDD[_] =>
            logInfo("Removing blocks of RDD " + b + " of time " + time)
            b. removeBlocks ()
          case _ =>
        }
      }
    }
    logDebug("Cleared " + oldRDDs.size + " RDDs that were older than " +
      (time - rememberDuration) + ": " + oldRDDs.keys.mkString(", "))
    dependencies.foreach(_. clearMetadata (time))
  }

spark.streaming.unpersist的配置可以用来设置是否手动清理。
想跨batch duration的话,可以设置rememberDuration。
其中把RDD清理掉了。依赖也清理掉了。
BlockRDD.removeBlocks:

  /**
   * Remove the data blocks that this BlockRDD is made from. NOTE: This is an
   * irreversible operation, as the data in the blocks cannot be recovered back
   * once removed. Use it with caution.
   */
  private[spark] def removeBlocks() {
    blockIds.foreach { blockId =>
      sparkContext.env.blockManager.master.removeBlock(blockId)
    }
    _isValid = false
  }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值