Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比

Spark Streaming

众所周知,Spark Streaming中的数据结构是DStream,是对RDD的进一步的封装,是”微批“的准实时处理。将连续的流数据转换为连续的批作业,这里的“批”,就是指RDD序列——DStream。

除了文件数据流之外,所有的输入DStream都会被绑定到一个Receiver对象上,Receiver接受数据。所以一个Spark Streaming程序,至少需要占用2个cpu core,一个给spark streaming application的executor,另一个给Receiver。

程序开发流程:

  1. DStream数据源
    (Flink中:Source)(Flink、Spark Streaming中,DStream/Source都可以是并行多个的。)
  2. Transformation+Action操作
    (Flink为Transformation)
  3. 调用StreamingContext的start开始执行程序
    (Flink为ExecutionEnvironment的execute()方法)
  4. stop

Spark Structed Streaming

之前在实时大数据平台技术选型概要这篇文章中有提到:

Structured Streaming是Spark2提出的新的实时流框架(2.0和2.1是实验版本,从Spark2.2开始为稳定版本),Spark2 将流式计算也统一到DataFrame里去。
Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比_第1张图片

总的来说,Structured Streaming 的意义有:

  • 重新抽象了流式计算
  • 易于实现数据的exactly-once(2.0之前的Spark Streaming 只能做到at-least once)
  • 解决了Spark Streaming存在的代码升级,DAG图变化引起的任务失败,无法断点续传的问题
  • 保证与批处理作业的强一致性
  • API简化(引入和流函数的封装。
    举个例子:Kafka中读取的数据,通过stream方法形成流,就可以直接与jdbc中读取的数据在DataSet层面就进行Join,不用使用transform或者foreachRDD方法。
    stream方法底层依赖Dataset和Dataframe,集成了SparkSql和Dataset几乎所有的功能,把流处理的代码编写一下子简化了很多。)
  • 基于Event-Time,相比于Spark Streaming的Processing-Time更精确
  • 未来将支持使用SQL和JDBC来实时查询Streaming data

通俗的说,以前只是流式计算,数据计算完之后放入外部存储,现在向持续计算发展,希望通过一套API实现数据的持续计算(数据在计算时,既可以结合离线数据,又可以提供实时查询,又可以与外部存储比如redis、hbase交互)。

持续计算

自Spark 2.3以来,引入了一种称为连续处理的新型低延迟处理模式,它可以实现低至1毫秒的端到端延迟,并且具有至少一次保证(at-least-once)。

无边界表

以下内容翻译自:Introducing Low-latency Continuous Processing Mode in Structured Streaming in Apache Spark 2.3

在Spark2.0中,Structured Streaming将微批处理模式与其高级API分离开来。原因有两个:首先,简化了API的使用,屏蔽了底层的微批处理细节。其次,Structured Streaming允许开发人员将流数据看做一个无界表,并且查询体验和静态表一致。

为了充分利用这一优势,在Spark2.3.0中引入一种被称为连续模式的可以实现毫秒级延迟的流处理模式。
Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比_第2张图片
Structured Streaming默认使用微批处理执行模型。 这意味着Spark流式计算引擎会定期检查流数据源,并对自上一批次结束后到达的新数据执行批量查询。 在高层次上,它看起来像这样。
Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比_第3张图片

  • Driver驱动程序通过将记录偏移量保存到预写日志中来对数据处理进度设置检查点
  • 当前到达的数据需要等待当前的微批处理作业完成,且其中数据的偏移量范围被计入日志后,才能在下一个微批作业中得到处理。

连续处理(Continuous Processing)
连续处理是Spark 2.3中引入的一种新的实验性流执行模式,可实现低(~1 ms)端到端延迟,并且至少具有一次容错保证。
在连续处理模式中,Spark不再是启动定期任务,而是启动一系列连续读取,处理和写入数据的长时间运行的任务。 在高层次上,设置和记录级时间线看起来像这些(与上述微量批处理执行图的对比)。
Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比_第4张图片

Spark Structed Streaming的一些主要特性及与Spark Streaming之间的对比_第5张图片
由于事件在到达时会被立即处理和写入结果,所以端到端延迟只有几毫秒。

此外,我们利用著名的Chandy-Lamport算法对查询进度设置检查点。 特殊标记的记录被注入到每个任务的输入数据流中; 我们将它们称为“时间代标记(epoch marker)”,并将它们之间的差距称为“时间代(epoch)”。当任务遇到标记时,任务异步报告处理后的最后偏移量给driver。 一旦driver程序接收到写入接收器的所有任务的偏移量,它就会将它们写入前述的预写日志。 由于检查点的设置是完全异步的,任务可以不间断地持续并提供一致的毫秒级延迟。

在Spark2.3.0中,流数据的连续处理模式还是一种实验性功能,在此模式下支持Structured Streaming所支持的所有流数据源以及DataFrame / Dataset / SQL操作的子集。


官方给的Spark Structed Streaming示例代码倒是与Spark Streaming没区别:

object StructuredNetworkWordCount {
  def main(args: Array[String]) {
    if (args.length < 2) {
      System.err.println("Usage: StructuredNetworkWordCount  ")
      System.exit(1)
    }

    val host = args(0)
    val port = args(1).toInt

    val spark = SparkSession
      .builder
      .appName("StructuredNetworkWordCount")
      .getOrCreate()

    import spark.implicits._

    // Create DataFrame representing the stream of input lines from connection to host:port
    val lines = spark.readStream
      .format("socket")
      .option("host", host)
      .option("port", port)
      .load()

    // Split the lines into words
    val words = lines.as[String].flatMap(_.split(" "))

    // Generate running word count
    val wordCounts = words.groupBy("value").count()

    // Start running the query that prints the running counts to the console
    val query = wordCounts.writeStream
      .outputMode("complete")
      .format("console")
      .start()

    query.awaitTermination()
  }
}

输出模式

有几种类型的输出模式:

  • 追加模式(默认) :
    这是默认模式,其中只有自上次触发后新添加到结果表中的行会输出到接收器。
    仅支持那些添加到结果表中的行永远不会更改的查询。因此,该模式保证每行仅输出一次(假设容错接收器)。例如,select,where,map,flatMap,filter,join等查询支持Append模式。
  • 完成模式 :
    每次触发后,整个结果表将输出到接收器。聚合查询支持此功能。
  • 更新模式:
    自Spark 2.1.1起可用)仅将结果表中自上次触发后更新的行输出到接收器。
 val query = wordCounts.writeStream
      .outputMode("complete") //与.outputMode(OutputMode.Complete)等价
      .format("console")
      .start()

与追加模式的差别,还不是很理解?
追加和完成比较容易理解(完成理解为完整更佳),比如说第一秒输入:
abcd
第二秒输入:
efgh
如果是收集信息后打印,那么完成模式会打印abcdefgh,而追加更新模式是efgh。
但是更新模式,不是很理解,

参考

  1. 在Apache Spark 2.3中引入结构化流中的低延迟连续处理模式
  2. doc: structured-streaming-programming-guide

你可能感兴趣的:(Hadoop,Spark,Hbase...)