• Spark Streaming源码解读之Driver容错安全性


    本节的主要内容:

    一、ReceivedBlockTracker容错安全性

    二、DStreamGraph和JobGenerator容错安全性

    从数据层面,ReceivedBlockTracker为整个Spark Streaming应用程序记录元数据信息。

    从调度层面,DStreamGraph和JobGenerator是Spark Streaming调度的核心,记录当前调度到哪一进度,和业务有关。

    ReceivedBlockTracker在接收到元数据信息后调用addBlock方法,先写入磁盘中,然后在写入内存,

    看源码:

    private type ReceivedBlockQueue = mutable.Queue[ReceivedBlockInfo]
    private val streamIdToUnallocatedBlockQueues = new mutable.HashMap[Int, ReceivedBlockQueue]
    private val timeToAllocatedBlocks = new mutable.HashMap[Time, AllocatedBlocks]
    private val writeAheadLogOption = createWriteAheadLog()
    private var lastAllocatedBatchTime: Time = null
    // Recover block information from write ahead logs
    if (recoverFromWriteAheadLog) {
    recoverPastEvents()
    }

    /** Add received block. This event will get written to the write ahead log (if enabled). */
    def addBlock(receivedBlockInfo: ReceivedBlockInfo): Boolean = {
    try {
    val writeResult = writeToLog(BlockAdditionEvent(receivedBlockInfo))
    if (writeResult) {
    synchronized {
    getReceivedBlockQueue(receivedBlockInfo.streamId) += receivedBlockInfo
    }
    logDebug(s"Stream ${receivedBlockInfo.streamId} received " +
    s"block ${receivedBlockInfo.blockStoreResult.blockId}")
    } else {
    logDebug(s"Failed to acknowledge stream ${receivedBlockInfo.streamId} receiving " +
    s"block ${receivedBlockInfo.blockStoreResult.blockId} in the Write Ahead Log.")
    }
    writeResult
    } catch {
    case NonFatal(e) =>
    logError(s"Error adding block $receivedBlockInfo", e)
    false
    }
    }
    根据batchTime分配属于当前BatchDuration要处理的数据到timToAllocatedBlocks数据结构,看源码:
    /**
    * Allocate all unallocated blocks to the given batch.
    * This event will get written to the write ahead log (if enabled).
    */
    def allocateBlocksToBatch(batchTime: Time): Unit = synchronized {
    if (lastAllocatedBatchTime == null || batchTime > lastAllocatedBatchTime) {
    val streamIdToBlocks = streamIds.map { streamId =>
    (streamId, getReceivedBlockQueue(streamId).dequeueAll(x => true))
    }.toMap
    val allocatedBlocks = AllocatedBlocks(streamIdToBlocks)
    if (writeToLog(BatchAllocationEvent(batchTime, allocatedBlocks))) {
    timeToAllocatedBlocks.put(batchTime, allocatedBlocks)
    lastAllocatedBatchTime = batchTime //上一个job分配完数据后在接下来分配
    } else {
    logInfo(s"Possibly processed batch $batchTime need to be processed again in WAL recovery")
    }
    } else {
    // This situation occurs when:
    // 1. WAL is ended with BatchAllocationEvent, but without BatchCleanupEvent,
    // possibly processed batch job or half-processed batch job need to be processed again,
    // so the batchTime will be equal to lastAllocatedBatchTime.
    // 2. Slow checkpointing makes recovered batch time older than WAL recovered
    // lastAllocatedBatchTime.
    // This situation will only occurs in recovery time.
    logInfo(s"Possibly processed batch $batchTime need to be processed again in WAL recovery")
    }
    }

    /** Get the blocks allocated to the given batch. */
    def getBlocksOfBatch(batchTime: Time): Map[Int, Seq[ReceivedBlockInfo]] = synchronized {
    timeToAllocatedBlocks.get(batchTime).map { _.streamIdToAllocatedBlocks }.getOrElse(Map.empty)
    }
    跟踪Time对象,ReceiverTracker的allocateBlocksToBatch方法中的入参batchTime是被JobGenerator的generateJobs方法调用的,看源码:
    /** Allocate all unallocated blocks to the given batch. */
    def allocateBlocksToBatch(batchTime: Time): Unit = {
    if (receiverInputStreams.nonEmpty) {
    receivedBlockTracker.allocateBlocksToBatch(batchTime)
    }
    }

    /** Get the blocks for the given batch and all input streams. */
    def getBlocksOfBatch(batchTime: Time): Map[Int, Seq[ReceivedBlockInfo]] = {
    receivedBlockTracker.getBlocksOfBatch(batchTime)
    }
    JobGenerator的generateJobs方法是被定时器发送GenerateJobs消息调用的,看源码:
    /** Generate jobs and perform checkpoint for the given `time`.  */
    private def generateJobs(time: Time) {
    // Set the SparkEnv in this thread, so that job generation code can access the environment
    // Example: BlockRDDs are created in this thread, and it needs to access BlockManager
    // Update: This is probably redundant after threadlocal stuff in SparkEnv has been removed.
    SparkEnv.set(ssc.env)
    Try {
    jobScheduler.receiverTracker.allocateBlocksToBatch(time) // allocate received blocks to batch
    graph.generateJobs(time) // generate jobs using allocated block
    } match {
    case Success(jobs) =>
    val streamIdToInputInfos = jobScheduler.inputInfoTracker.getInfo(time)
    jobScheduler.submitJobSet(JobSet(time, jobs, streamIdToInputInfos))
    case Failure(e) =>
    jobScheduler.reportError("Error generating jobs for time " + time, e)
    }
    eventLoop.post(DoCheckpoint(time, clearCheckpointDataLater = false))
    }
    GenerateJobs中的时间参数就是nextTime,而nextTime+=period,这个period就是ssc.graph.batchDuration.milliseconds:
    private def triggerActionForNextInterval(): Unit = {
    clock.waitTillTime(nextTime)
    callback(nextTime)
    prevTime = nextTime
    nextTime += period
    logDebug("Callback for " + name + " called at time " + prevTime)
    }
    nextTime的初始值是在start方法中传入的startTime赋值的,即RecurringTimer的getStartTime方法的返回值,是当前时间period的(整数倍+1):
    /** Starts the generator for the first time */
    private def startFirstTime() {
    val startTime = new Time(timer.getStartTime())
    graph.start(startTime - graph.batchDuration)
    timer.start(startTime.milliseconds)
    logInfo("Started JobGenerator at " + startTime)
    }
    Period这个值是我们调用new StreamingContext来构造StreamingContext时传入的Duration值:
    def setBatchDuration(duration: Duration) {
    this.synchronized {
    require(batchDuration == null,
    s"Batch duration already set as $batchDuration. Cannot set it again.")
    batchDuration = duration
    }
    }
    private[streaming] val graph: DStreamGraph = {
    if (isCheckpointPresent) {
    cp_.graph.setContext(this)
    cp_.graph.restoreCheckpointData()
    cp_.graph
    } else {
    require(batchDur_ != null, "Batch duration for StreamingContext cannot be null")
    val newGraph = new DStreamGraph()
    newGraph.setBatchDuration(batchDur_)
    newGraph
    }
    }
    ReceivedBlockTracker会清除过期的元数据信息,从HashMap中移除,也是先写入磁盘,然后在写入内存。
    元数据的生成,消费和销毁都有WAL,所以失败时就可以从日志中恢复。从源码分析中得出只有设置了checkpoint目录,才进行WAL机制。

    总结:

    ReceivedBlockTracker是通过WAL方式来进行数据容错的。

    DStreamGraph和JobGenerator是通过checkpoint方式来进行数据容错的。

    Spark发行版笔记13

    新浪微博:http://weibo.com/ilovepains

    微信公众号:DT_Spark

    博客:http://blog.sina.com.cn/ilovepains

    手机:18610086859

    QQ:1740415547

    邮箱:18610086859@vip.126.com

    
    
  • 相关阅读:
    html的转码玉反转码
    获取url据对路径写法
    CSS 外边距合并
    页面禁制选中元素的 背景变蓝的通用写法
    centos7.3上安装oracle11.2.4RAC
    通过ansible检查所有服务器根目录磁盘使用情况
    解决es集群启动完成后报master_not_discovered_exception(hostname有错误)
    tidb4.0执行大型sql报没有tmp目录错处理(ERROR 1105 (HY000): open /tmp/1000_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA)
    aix磁盘创建pv、lv
    aix6.1安装oracle
  • 原文地址:https://www.cnblogs.com/sparkbigdata/p/5517359.html
Copyright © 2020-2023  润新知