Spark Streaming 1.6.0中的Checkpointing / WAL的可靠性问题

时间:2016-01-19 14:15:54

标签: scala apache-spark spark-streaming amazon-kinesis checkpointing

描述

我们在Scala中有一个Spark Streaming 1.5.2应用程序,它从Kinesis Stream中读取JSON事件,执行一些转换/聚合并将结果写入不同的S3前缀。当前批处理间隔为60秒。我们有3000-7000事件/秒。我们正在使用检查点来保护我们免于丢失聚合。

它已经运行了一段时间,从异常甚至群集重启中恢复。我们最近重新编译了Spark Streaming 1.6.0的代码,只更改了 build.sbt 文件中的库依赖项。在Spark 1.6.0群集中运行代码几个小时后,我们注意到以下内容:

  1. “投入率”和“处理时间”波动率在1.6.0中大幅增加(见下面的截图)。
  2. 每隔几个小时,在写记录时会抛出一个'Exception:BlockAdditionEvent ...到WriteAheadLog。 java.util.concurrent.TimeoutException:[5000毫秒]之后的期货超时“异常(参见下面的完整堆栈跟踪)与特定批次(分钟)的0事件/秒下降一致。
  3. 在做了一些挖掘之后,我认为第二个问题看起来与这个Pull Request有关。 PR的最初目标:“当使用S3作为WALs的目录时,写入时间太长。当多个接收器将AddBlock事件发送到ReceiverTracker时,驱动程序很容易受到瓶颈。此PR在ReceivedBlockTracker中添加事件批处理,以便接收器不会被驱动程序阻止太长时间。“

    我们在Spark 1.5.2中的S3中检查点,并且没有性能/可靠性问题。我们在S3和本地NAS中测试了Spark 1.6.0中的检查点,在这两种情况下我们都收到了这个例外。看起来当检查点批次需要超过5秒时,会出现此异常,并且我们已检查该批次的事件是否永远丢失。

    问题

    • Spark Streaming 1.6.0中预计“输入率”和“处理时间”波动的增加是否有任何改进方法?

    • 除了这2个以外,您知道任何解决方法吗?:

      1)确保检查点接收器写入所有文件所需的时间少于5秒。根据我的经验,即使是小批量,也无法保证使用S3。对于本地NAS,它取决于谁负责基础设施(云提供商很难)。

      2)增加spark.streaming.driver.writeAheadLog.batchingTimeout属性值。

    • 您是否希望在所描述的场景中丢失任何事件?我认为如果批量检查点失败,则碎片/接收器序列号不会增加,并且会在以后重试。

    Spark 1.5.2 Statistics - 截图

    enter image description here

    Spark 1.6.0统计 - 屏幕截图

    enter image description here

    完整堆栈跟踪

    16/01/19 03:25:03 WARN ReceivedBlockTracker: Exception thrown while writing record: BlockAdditionEvent(ReceivedBlockInfo(0,Some(3521),Some(SequenceNumberRanges(SequenceNumberRange(StreamEventsPRD,shardId-000000000003,49558087746891612304997255299934807015508295035511636018,49558087746891612304997255303224294170679701088606617650), SequenceNumberRange(StreamEventsPRD,shardId-000000000004,49558087949939897337618579003482122196174788079896232002,49558087949939897337618579006984380295598368799020023874), SequenceNumberRange(StreamEventsPRD,shardId-000000000001,49558087735072217349776025034858012188384702720257294354,49558087735072217349776025038332464993957147037082320914), SequenceNumberRange(StreamEventsPRD,shardId-000000000009,49558088270111696152922722880993488801473174525649617042,49558088270111696152922722884455852348849472550727581842), SequenceNumberRange(StreamEventsPRD,shardId-000000000000,49558087841379869711171505550483827793283335010434154498,49558087841379869711171505554030816148032657077741551618), SequenceNumberRange(StreamEventsPRD,shardId-000000000002,49558087853556076589569225785774419228345486684446523426,49558087853556076589569225789389107428993227916817989666))),BlockManagerBasedStoreResult(input-0-1453142312126,Some(3521)))) to the WriteAheadLog.
    java.util.concurrent.TimeoutException: Futures timed out after [5000 milliseconds]
        at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219)
        at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:223)
        at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:107)
        at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
        at scala.concurrent.Await$.result(package.scala:107)
        at org.apache.spark.streaming.util.BatchedWriteAheadLog.write(BatchedWriteAheadLog.scala:81)
        at org.apache.spark.streaming.scheduler.ReceivedBlockTracker.writeToLog(ReceivedBlockTracker.scala:232)
        at org.apache.spark.streaming.scheduler.ReceivedBlockTracker.addBlock(ReceivedBlockTracker.scala:87)
        at org.apache.spark.streaming.scheduler.ReceiverTracker.org$apache$spark$streaming$scheduler$ReceiverTracker$$addBlock(ReceiverTracker.scala:321)
        at org.apache.spark.streaming.scheduler.ReceiverTracker$ReceiverTrackerEndpoint$$anonfun$receiveAndReply$1$$anon$1$$anonfun$run$1.apply$mcV$sp(ReceiverTracker.scala:500)
        at org.apache.spark.util.Utils$.tryLogNonFatalError(Utils.scala:1230)
        at org.apache.spark.streaming.scheduler.ReceiverTracker$ReceiverTrackerEndpoint$$anonfun$receiveAndReply$1$$anon$1.run(ReceiverTracker.scala:498)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    

    源代码提取

    ...
         // Function to create a new StreamingContext and set it up
      def setupContext(): StreamingContext = {
        ...
        // Create a StreamingContext
        val ssc = new StreamingContext(sc, Seconds(batchIntervalSeconds))
    
        // Create a Kinesis DStream
        val data = KinesisUtils.createStream(ssc,
          kinesisAppName, kinesisStreamName,
          kinesisEndpointUrl, RegionUtils.getRegionByEndpoint(kinesisEndpointUrl).getName(),
          InitialPositionInStream.LATEST, Seconds(kinesisCheckpointIntervalSeconds),
          StorageLevel.MEMORY_AND_DISK_SER_2, awsAccessKeyId, awsSecretKey)
    ...
        ssc.checkpoint(checkpointDir)
    
        ssc
      }
    
    
      // Get or create a streaming context.
      val ssc = StreamingContext.getActiveOrCreate(checkpointDir, setupContext)
    
      ssc.start()
      ssc.awaitTermination()
    

1 个答案:

答案 0 :(得分:4)

按照zero323的建议将评论作为答案发布:

增加spark.streaming.driver.writeAheadLog.batchingTimeout解决了检查点超时问题。在确定我们有空间之后我们做到了。我们已经测试了一段时间了。因此我只建议在仔细考虑后增加它。

<强>详情

我们在$ SPARK_HOME / conf / spark-defaults.conf中使用了这两个设置:

spark.streaming.driver.writeAheadLog.allowBatching true    spark.streaming.driver.writeAheadLog.batchingTimeout 15000

最初,我们只将spark.streaming.driver.writeAheadLog.allowBatching设置为true。

在更改之前,我们已经在测试环境中重现了问题中提到的问题(&#34; ... ReceivedBlockTracker:在编写记录时抛出异常......&#34;)。它每隔几个小时发生一次。改变之后,问题就消失了。我们将它运行了几天才开始生产。

我们发现getBatchingTimeout() method of the WriteAheadLogUtils类的默认值为5000毫秒,如下所示:

def getBatchingTimeout(conf: SparkConf): Long = {
    conf.getLong(DRIVER_WAL_BATCHING_TIMEOUT_CONF_KEY, defaultValue = 5000)
}