Spark Streaming中的checkpoint

Checkpoint

我们必须记录一些信息以方便恢复现场,在Spark Streaming中使用checkpoint实现恢复操作。

Spark Streaming中有两种不同对象的checkpont操作。

元数据(Metadata) checkpointing

保存Streaming中定义流计算的相关信息到可信赖的文件系统如HDFS,这种方式用于运行Driver的节点失败之后的恢复。

元数据包括:

  • 配置---创建context时的配置
  • DStream的操作---定义流应用程序的DStream操作集
  • 未完成的批次---记录已经在队列中但是还没有完成的批次

数据(Data) checkpointing

保存生成的RDD到可信赖的存储中,有一种场景是一些有状态的transformations是跨多个批次的数据组合,生成的RDD依赖上个批次的RDD,导致RDD的依赖链随着程序运行越来越长,当程序失败后,因为依赖链过长,恢复需要相当长的时间,如果定期checkponit就可以切断依赖链以减少恢复时间。
注意:切断RDD之间的依赖链只是Data checkpointing顺带解决的问题,而不是设计Data checkpointing的目的,Data checkpointing的目的和作用还是保存RDD到可信赖的存储。

总的来说,元数据checkpoint主要是为了driver失败后的恢复,然而,如果使用上面提及的stateful transformations,RDD的checkpoint也是必要的。

checkpoint应用场景

checkpoint在以下场景中是必要的:

  1. stateful transformations:在使用updateStateByKey, reduceByKeyAndWindow这类算子之后必须使用checkpoint
  2. 从运行应用程序的驱动程序的故障中恢复——元数据检查点用于恢复进度信息。

checkpoint的使用

对于上面提及的Stateful checkpoint(第2类),可以直接使用streamingContext.checkpoint(checkpointDirectory)即可

对于方便driver恢复的checkpoint(第1类),在应用程序中必须满足以下行为:

  • 当应用程序一启动,将创建StreamingContext,配置流信息然后start()
  • 当应用程序失败之后的重新启动,将会从checkpoint目录中的checkpoint数据中恢复StreamingContext。

上面的两种行为可以用StreamingContext.getOrCreate完成。

  // Function to create and setup a new StreamingContext

  def functionToCreateContext(): StreamingContext = {

    val ssc = new StreamingContext(...)   // new context

    val lines = ssc.socketTextStream(...) // create DStreams

    ...

    ssc.checkpoint(checkpointDirectory)   // set checkpoint directory

    ssc

  }

  // Get StreamingContext from checkpoint data or create a new one

  val context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext _)

  // Do additional setup on context that needs to be done,// irrespective of whether it is being started or restarted

  context. ...

  // Start the context

  context.start()

  context.awaitTermination()

当checkpoint目录不存在时,将重新创建context。

累加器与广播变量

累加器与广播变量不能直接从checkpoint中恢复,如果想使用累加器与广播变量,可以借助单例对象,创建一个懒加载的单例实例对象保存累加器与广播变量,当driver由于失败重新启动时,将会重新实例化这些懒加载的单例对象。

  object WordBlacklist {

    @volatile private var instance: Broadcast[Seq[String]] = null

    def getInstance(sc: SparkContext): Broadcast[Seq[String]] = {

      if (instance == null) {

        synchronized {

          if (instance == null) {

            val wordBlacklist = Seq("a", "b", "c")

            instance = sc.broadcast(wordBlacklist)

          }

        }

      }

      instance

    }}

  object DroppedWordsCounter {

    @volatile private var instance: LongAccumulator = null

    def getInstance(sc: SparkContext): LongAccumulator = {

      if (instance == null) {

        synchronized {

          if (instance == null) {

            instance = sc.longAccumulator("WordsInBlacklistCounter")

          }

        }

      }

      instance

    }}

  wordCounts.foreachRDD { (rdd: RDD[(String, Int)], time: Time) =>

    // Get or register the blacklist Broadcast

    val blacklist = WordBlacklist.getInstance(rdd.sparkContext)

    // Get or register the droppedWordsCounter Accumulator

    val droppedWordsCounter = DroppedWordsCounter.getInstance(rdd.sparkContext)

    // Use blacklist to drop words and use droppedWordsCounter to count them

    val counts = rdd.filter { case (word, count) =>

      if (blacklist.value.contains(word)) {

        droppedWordsCounter.add(count)

        false

      } else {

        true

      }

    }.collect().mkString("[", ", ", "]")

    val output = "Counts at time " + time + " " + counts})

猜你喜欢

转载自blog.csdn.net/chroje/article/details/80565642