Spark Streaming

SparkStreaming简介

  • 用于流式数据的处理

  • Spark Streaming具有有高吞吐量和容错能力强等特点

  • 支持多数据源输入,eg:Kafka,Flume,Twitter和TCP套接字等

  • 数据输入之后可以使用高阶API

  • 处理结果可保存到多存储介质中

  • SparkStreaming能与MLlib以及Graphx融合

  • Spark Streaming使用离散化流(Discretized Stream)作为抽象表示,称为DStream。

  • DStream是随时间推移而收到的数据的序列

  • DStream 可以从Flume,Kafka,HDFS等多种输入源创建

  • DStream 支持两种操作:

  • 转化操作,会生成一个新的DStream

  • 输出操作(output operation),把数据写入外部系统中

  • DStream 提供了许多与 RDD 所支持的操作相类似的操作支持,还增加了与时间相关的新操作,比如滑动窗口

架构

  • Spark Streaming使用 mini-batch 的架构,把流式计算当作一系列连续的小规模批处理来对待
  • Spark Streaming的编程抽象是离散化流,也就是DStream。它是一个 RDD 序列,每个RDD代表数据流中一个时间片内的数据
  • 应用于 DStream 上的转换操作都会转换为底层RDD上的操作
  • 这些底层的RDD转换是由Spark引擎完成的,开发人员使用高级API完成数据操作。
  • Spark Streaming为每个输入源启动对应的接收器。接收器运行在Executor中,从输入源收集数据并保存为 RDD
  • 默认情况下接收到的数据后会复制到另一个Executor中,进行容错;
  • Driver 中的 StreamingContext 会周期性地运行 Spark 作业来处理这些数据。

Spark Streaming 运行流程

  • 1、客户端提交Spark Streaming作业后启动Driver,Driver启动Receiver,Receiver接收数据源的数据
  • 2、每个作业包含多个Executor,每个Executor以线程的方式运行task,SparkStreaming至少包含一个receiver task(一般情况下)
  • 3、Receiver接收数据后生成Block,并把BlockId汇报给Driver,然后备份到另外一个 Executor 上
  • 4、ReceiverTracker维护 Reciver 汇报的BlockId
  • 5、Driver定时启动JobGenerator,根据Dstream的关系生成逻辑RDD,然后创建Jobset,交给JobScheduler
  • 6、JobScheduler负责调度Jobset,交给DAGScheduler,DAGScheduler根据逻辑RDD,生成相应的Stages,每个stage包含一到多个Task,将TaskSet提交给TaskSchedule
  • 7、TaskScheduler负责把 Task 调度到 Executor 上,并维护 Task 的运行状态

优缺点

优点

  • Spark Streaming 内部的实现和调度方式高度依赖 Spark 的 DAG 调度器和RDD,所以SparkStreaming必须是粗粒度方式的设计。同时,由于 Spark 内部调度器足够快速和高效,可以快速地处理小批量数据,这就获得准实时的特性
  • Spark Streaming 的粗粒度执行方式使其确保 ”处理且仅处理一次” 的特性(EOS),同时也可以更方便地实现容错恢复机制
  • 由于 Spark Streaming 的 DStream 本质是 RDD 在流式数据上的抽象,因此基于 RDD 的各种操作也有相应的基于 DStream 的版本,可降低了学习成本,并能在spark的基础上快速掌握streaming
  • 由于 DStream 是在 RDD 上的抽象,那么也就更容易与 RDD 进行交互操作,能够方便的将流式数据和批处理数据结合进行分析

缺点

  • Spark Streaming 的粗粒度处理方式也造成了不可避免的延迟。在细粒度处理方式下,理想情况下每一条记录都会被实时处理,而在 Spark Streaming 中,数据需要汇总到一定的量后再一次性处理,这就增加了数据处理的延迟,这种延迟是由框架的设计引入的,并不是由网络或其他情况造成的

Structured Streaming

  • Spark Streaming计算逻辑是把数据按时间划分为DStream,存在以下问题

  • 框架自身只能根据 Batch Time 单元进行数据处理,很难处理基于eventtime(即时间戳)的数据,很难处理延迟,乱序的数据

  • 流式和批量处理的 API 不完全一致,两种使用场景中,程序代码还是需要一定的转换

  • 端到端的数据容错保障逻辑需要用户自己构建,难以处理增量更新和持久化存储等一致性问题

  • 基于以上问题,提出了下一代 Structure Streaming 。将数据源映射为一张无界长度的表,通过表的计算,输出结果映射为另一张表

  • 以结构化的方式去操作流式数据,简化了实时计算过程,同时还复用了 Catalyst 引擎来优化SQL操作。此外还能支持增量计算和基于event time的计算。

DStream基础数据源

文件数据流:通过 textFileStream(directory) 方法进行读取 HDFS 兼容的文件系统文件

Spark Streaming 将会监控 directory 目录,并不断处理移动进来的文件

  • 不支持嵌套目录
  • 文件需要有相同的数据格式
  • 文件进入 directory 的方式需要通过移动或者重命名来实现
  • 一旦文件移动进目录,则不能再修改,即便修改了也不会读取新数据
  • 文件流不需要接收器(receiver),不需要单独分配CPU核

Socket数据流

RDD队列流:可使用streamingContext.queueStream(queueOfRDD) 创建基于RDD队列的DStream

DStream转换操作

DStream上的操作与RDD的类似,分为 Transformations(转换)和 OutputOperations(输出)两种,此外转换操作中还有一些比较特殊的方法,如:updateStateByKey、transform 以及各种 Window 相关的操作

注意

  • 在DStream与RDD上的转换操作非常类似(无状态的操作)
  • DStream有自己特殊的操作(窗口操作、追踪状态变化操作)
  • 在DStream上的转换操作比RDD上的转换操作少

DStream 的转化操作可以分为 无状态(stateless) 和 有状态(stateful) 两种:

  • 无状态转化操作。每个批次的处理不依赖于之前批次的数据。常见的 RDD 转化操作,例如 map、filter、reduceByKey 等
  • 有状态转化操作。需要使用之前批次的数据 或者是 中间结果来计算当前批次的数据。有状态转化操作包括:基于滑动窗口的转化操作 或 追踪状态变化的转化操作

无状态转换

  • 无状态转化操作就是把简单的 RDD 转化操作应用到每个批次上,也就是转化
    DStream 中的每一个 RDD
  • 常见的无状态转换包括:map、flatMap、filter、repartition、reduceByKey、
    groupByKey;直接作用在DStream上

有状态转换

  • 有状态的转换主要有两种:窗口操作、状态跟踪操作

  • 窗口操作

  • Window Operations可以设置窗口大小和滑动窗口间隔来动态的获取当前Streaming的状态。

  • 基于窗口的操作需要两个参数:

    • 窗口长度(windowDuration)。控制每次计算最近的多少个批次的数据
    • 滑动间隔(slideDuration)。用来控制对新的 DStream 进行计算的间隔
  • 两者都必须是 StreamingContext 中批次间隔(batchDuration)的整数倍

  • updateStateByKey(状态追踪操作),主要功能

  • 为Streaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是自定义对象;更新函数也可以是自定义的

  • 通过更新函数对该key的状态不断更新,对于每个新的batch而言,SparkStreaming会在使用updateStateByKey 的时候为已经存在的key进行state的状态更新

  • 使用 updateStateByKey 时要开启 checkpoint 功能

DStream输出操作

如果 StreamingContext 中没有设定输出操作,整个流式作业不会启动。

通用的输出操作 foreachRDD,用来对 DStream 中的 RDD 进行任意计算。在foreachRDD中,可以重用 Spark RDD 中所有的 Action 操作。需要注意的:

  • 连接不要定义在 Driver 中
  • 连接定义在 RDD的 foreach 算子中,则遍历 RDD 的每个元素时都创建连接,得不偿失
  • 应该在 RDD的 foreachPartition 中定义连接,每个分区创建一个连接
  • 可以考虑使用连接池

SparkStreaming与Kafka整合

针对不同的spark、kafka版本,集成处理数据的方式分为两种:Receiver Approach和Direct Approach

不同集成版本处理方式的支持,如下图所示

对Kafka的支持分为两个版本08(在高版本中将被废弃)、010,两个版本不兼容

Kafka-08 接口

  • Receiver based Approach

  • Kafka-08 接口(Receiver方式):

    • Offset保存在ZK中,系统管理
    • 对应Kafka的版本 0.8.2.1+
    • 接口底层实现使用 Kafka 旧版消费者高阶API
    • DStream底层实现为BlockRDD
  • Kafka-08 接口(Receiver with WAL)

    • 增强了故障恢复的能力
    • 接收的数据与Dirver的元数据保存到HDFS
    • 增加了流式应用处理的延迟
  • Direct Approach

  • Direct Approach是 Spark Streaming不使用Receiver集成kafka的方式,在企业生产环境中使用较多。相较于Receiver,有以下特点:

    • 不使用 Receiver。减少不必要的CPU占用;减少了 Receiver接收数据写入BlockManager,然后运行时再通过blockId、网络传输、磁盘读取等来获取数据的整个过程,提升了效率;无需WAL,进一步减少磁盘IO;
    • Direct方式生的RDD是KafkaRDD,它的分区数与 Kafka 分区数保持一致,便于把控并行度
    • 注意:在 Shuffle 或 Repartition 操作后生成的RDD,这种对应关系会失效
    • 可以手动维护offset,实现 Exactly Once 语义
  • Kafka-010 接口

  • LocationStrategies(本地策略)

    • 添加依赖
      org.apache.spark spark-streaming-kafka-0- 10_2.12 ${spark.version}

    • LocationStrategies.PreferBrokers:如果 Executor 在 kafka 集群中的某些节点上,可以使用这种策略。此时Executor 中的数据会来自当前broker节点

    • LocationStrategies.PreferConsistent:大多数情况下使用的策略,将Kafka分区均匀的分布在Spark集群的 Executor上

    • LocationStrategies.PreferFixed:如果节点之间的分区有明显的分布不均,使用这种策略。通过一个map指定将 topic 分区分布在哪些节点中

    • ConsumerStrategies(消费策略)

      • ConsumerStrategies.Subscribe,用来订阅一组固定topic
      • ConsumerStrategies.SubscribePattern,使用正则来指定感兴趣的topic
      • ConsumerStrategies.Assign,指定固定分区的集合
      • 以上策略都有重载构造函数,允许指定特定分区的起始偏移量;使用 Subscribe

或 SubscribePattern 在运行时能实现分区自动发现

  • Offset 管理

  • 获取偏移量(Obtaining Offsets)

    • stream.foreachRDD { rdd =>
      val offsetRanges =
      rdd.asInstanceOf[HasOffsetRanges].offsetRanges
      rdd.foreachPartition { iter =>
      val o: OffsetRange =
      offsetRanges(TaskContext.get.partitionId)
      println(s"{o.topic}o.topic{o.partition} {o.fromOffset} <br />o.fromOffset<br/>{o.untilOffset}")
      }
      }
  • 存储偏移量(Storing Offsets)

    • 如果要实现EOS语义(Exactly Once Semantics),必须在幂等的输出之后存储偏移量或者 将存储偏移量与输出放在一个事务中。

    • 存储偏移量的方式

    • Checkpoint:Checkpoint是对Spark Streaming运行过程中的元数据和每RDDs的数据状态保存到一个持久化系统中,当然这里面也包含了offset,一般是HDFS、S3,如果应用程序或集群挂了,可以迅速恢复

    • Kafka:默认情况下,消费者定期自动提交偏移量,它将偏移量存储在一个特殊的Kafka主题中(__consumer_offsets)

      • 可以将 enable.auto.commit 设置为 false ,在 Streaming 程序输出结果之后,手动提交偏移到kafka。
    • 自定义存储

      • Offsets可以通过多种方式来管理,但是一般来说遵循下面的步骤:
      • 在 DStream 初始化的时候,需要指定每个分区的offset用于从指定位置读取数据
      • 读取并处理消息
      • 处理完之后存储结果数据
      • 用虚线圈存储和提交offset,强调用户可能会执行一系列操作来满足他们更加严格的语义要求。这包括幂等操作和通过原子操作的方式存储offset
      • 将 offsets 保存在外部持久化数据库如 HBase、Kafka、HDFS、ZooKeeper、Redis、MySQL … …
  • Redis管理的Offset

    • 要想将Offset保存到外部存储中,关键要实现以下几个功能:

      - Streaming程序启动时,从外部存储获取保存的Offsets(执行一次)
      
      - 在foreachRDD中,每个批次数据处理之后,更新外部存储的offsets(多次执行)
      - 引入依赖
          <dependency>
              <groupId>redis.clients</groupId>
              <artifactId>jedis</artifactId>
              <version>2.9.0</version>
          </dependency>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值