SparkStreaming

本文深入解析SparkStreaming的工作原理,包括其运行架构、模块划分、数据处理流程和容错机制。对比SparkStreaming、Flink和Storm,分析各自优势及应用场景。

SparkStreaming

  Spark Streaming 是基于Spark Core将流式计算分解成一系列的小批处理任务来执行。对于Spark Core它的核心就是RDD,对于Spark Streaming来说,它的核心是DStream,DStream实质上是一系列的RDD的集合,DStream可以按照秒数将数据流进行批量的划分。首先从接收到流数据之后,将其划分为多个batch,然后提交给Spark集群进行计算。在Spark Streaming里,总体负责任务的动态调度是JobScheduler,而JobScheduler有两个很重要的成员:JobGenerator和 ReceiverTracker。JobGenerator 负责将每个 batch 生成具体的 RDD DAG ,而ReceiverTracker负责数据的来源。

Spark Streaming 的整体模块划分为四个大的模块:
 模块 1:DAG 静态定义
 模块 2:Job 动态生成
 模块 3:数据产生与导入
 模块 4:长时容错

模块 1:DAG 静态定义
  将计算逻辑描述为一个RDD DAG的“模板”,在后面Job动态生成的时候,针对每个 batch,Spark Streaming 都将根据这个“模板”生成一个 RDD DAG 的实例。

DStream 和 DStreamGraph
  在 Spark Streaming 里,这个 RDD “模板”对应的具体的类是 DStream,RDD DAG“模板”对应的具体类是DStreamGraph,DStream 也通过 transformation 连接成 DStreamGraph。

DStream 和 RDD 的关系
  DStream和RDD具有相同的 transformation 操作,比如 map(), filter(), reduce() 等等,正是这些相同的 transformation 使得 DStreamGraph能够忠实记录 RDD DAG的计算逻辑,那RDD和DStream的不同之处在于:DStream 维护了对每个产出的 RDD 实例的引用。

模块 2:Job 动态生成
  现在有了 DStreamGraph 和 DStream,也就是静态定义了的计算逻辑,下面来看 Spark Streaming 是如何将其动态调度的。
  在 Spark Streaming 程序的入口,会定义一个 batchDuration,就是需要每隔多长时间就比照静态的 DStreamGraph 来动态生成一个 RDD DAG 实例。在 Spark Streaming 里,总体负责动态作业调度的具体类是 JobScheduler,在 Spark Streaming 程序开始运行的时候,会生成一个 JobScheduler 的实例,并被 start() 运行起来。
  JobScheduler 有两个非常重要的成员:JobGeneratorReceiverTracker。JobScheduler 将每个 batch 的 RDD DAG 具体生成工作委托给 JobGenerator,而将源头输入数据的记录工作委托给 ReceiverTracker。
  JobGenerator 维护了一个定时器,周期就是batchDuration,定时为每个 batch 生成 RDD DAG 的实例。具体的,每次 RDD DAG 实际生成包含 5 个步骤:

  1. 要求 ReceiverTracker 将目前已收到的数据进行一次 allocate,即将上次 batch 切分后的数据切分到到本次新的 batch 里
  2. 要求 DStreamGraph 复制出一套新的 RDD DAG 的实例,具体过程是:DStreamGraph 将要求图里的尾 DStream 节点生成具体的 RDD 实例,并递归的调用尾 DStream 的上游 DStream 节点……以此遍历整个 DStreamGraph,遍历结束也就正好生成了 RDD DAG 的实例
  3. 获取第 1 步 ReceiverTracker 分配到本 batch 的源头数据的 meta 信息
  4. 将第 2 步生成的本 batch 的 RDD DAG,和第 3 步获取到的 meta 信息,一同提交给 JobScheduler 异步执行
  5. 只要提交结束就马上对整个系统的当前运行状态做一个 checkpoint

模块 3:数据产生与导入
  DStream 有一个重要子类 ReceiverInputDStream:它除了需要像其它 DStream 那样在某个 batch 里实例化 RDD 以外,还需要额外的 Receiver 为这个 RDD 生产数据。具体的,Spark Streaming 在程序刚开始运行时:

(1) 由ReceiverTracker分发多个 job到多个 executor 上分别启动 ReceiverSupervisor 实例
(2) 每个 ReceiverSupervisor 启动后将生成一个用户提供的 Receiver实例,并在 Receiver 实例生成后调用 Receiver.onStart()

  这时 Receiver 启动工作已运行完毕。接下来 ReceiverSupervisor 将在 executor 端作为的主要角色,并且:

(3) Receiver在onStart() 启动后,就将持续不断地接收外界数据,并持续交给 ReceiverSupervisor 进行数据转储
(4) ReceiverSupervisor 持续不断地接收到 Receiver 转来的数据:如果数据很细小,就需要 BlockGenerator 攒多条数据成一块然后再成块存储,否则直接成块存储

  这里 Spark Streaming 目前支持两种成块存储方式,一种是由BlockManagerBasedBlockHandler 直接存到 executor 的内存或硬盘,另一种由 WriteAheadLogBasedBlockHandler同时写 WAL和 executor 的内存或硬盘

(5) 每次成块在 executor 存储完毕后,ReceiverSupervisor 就会及时上报块数据的 meta 信息给 driver 端的 ReceiverTracker
(6) ReceiverTracker 再将收到的块数据 meta 信息直接转给ReceivedBlockTracker,由 ReceivedBlockTracker 专门管理收到的块数据 meta 信息

  后续在 driver 端,就由 ReceiverInputDStream 在每个 batch 去检查 ReceiverTracker 收到的块数据 meta 信息,界定哪些新数据需要在本 batch 内处理,然后生成相应的 RDD 实例去处理这些块数据

模块 4:长时容错
  如何保障Spark Streaming的长时运行,即与前 3 个模块结合,保障前 3 个模块的长时运行。模块1和2需要在driver端完成,模块3需要在executor端和driver端完成。

executor 端长时容错
  在 executor 端,ReceiverSupervisor 和 Receiver 失效后直接重启就可以了,关键是保障收到的块数据的安全。保障了源头块数据,就能够保障 RDD DAG (Spark Core 的 lineage)重做。Spark Streaming 对源头块数据的保障,分为 4 个层次,全面、相互补充,又可根据不同场景灵活设置:

  1. 热备:热备是指在存储块数据时,将其存储到本 executor、并同时 replicate 到另外一个 executor 上去。这样在一个 replica 失效后,可以立刻无感知切换到另一份 replica 进行计算。实现方式是,在实现自己的 Receiver 时,即指定一下 StorageLevel 为 MEMORY_ONLY_2 或 MEMORY_AND_DISK_2 就可以了
  2. 冷备:冷备是每次存储块数据前,先把块数据作为 log 写出到 WriteAheadLog 里,再存储到本 executor。executor 失效时,就由另外的 executor 去读 WAL,再重做 log 来恢复块数据。WAL 通常写到可靠存储如 HDFS 上,所以恢复时可能需要一段 recover time
  3. 重放:如果上游支持重放,比如 Apache Kafka,那么就可以选择不用热备或者冷备来另外存储数据了,而是在失效时换一个 executor 进行数据重放即可
  4. 忽略:最后,如果应用的实时性需求大于准确性,那么一块数据丢失后我们也可以选择忽略、不恢复失效的源头数据

driver 端长时容错
  块数据的 meta 信息上报到 ReceiverTracker,然后交给 ReceivedBlockTracker 做具体的管理。ReceivedBlockTracker 也采用 WAL 冷备方式进行备份,在 driver 失效后,由新的 ReceivedBlockTracker 读取 WAL 并恢复 block 的 meta 信息。另外,需要定时对 DStreamGraph 和 JobScheduler 做 Checkpoint,来记录整个 DStreamGraph 的变化、和每个 batch 的 job 的完成情况。
  Checkpoint 发起的间隔默认的是和 batchDuration 一致;即每次 batch 发起、提交了需要运行的 job 后就做 Checkpoint,另外在 job 完成了更新任务状态的时候再次做一下 Checkpoint。这样一来,在 driver 失效并恢复后,可以读取最近一次的 Checkpoint 来恢复作业的 DStreamGraph 和 job 的运行及完成状态。

总结

模块长时容错保障方式
模块 1-DAG 静态定义driver 端定时对 DStreamGraph 做 Checkpoint,来记录整个 DStreamGraph 的变化
模块 2-job 动态生成driver 端定时对 JobScheduler 做 Checkpoint,来记录每个 batch 的 job 的完成情况
模块 3-数据产生与导入driver 端源头块数据的 meta 信息上报 ReceiverTracker 时,写入 WAL
模块 3-数据产生与导入executor 端对源头块数据的保障:(1) 热备;(2) 冷备;(3) 重放;(4) 忽略

Spark Streaming运行架构

  SparkStreaming分为Driver端和Client端,运行在Driver端的是StreamingContext实例。对于StreamingContext进行实例化的时候主要涉及到DStreamGraph和JobSchedule(包含ReceiverTracker和JobGenerator),而Client端主要完成对ReceiverSupervisor和Receiver相关的初始化。
  SparkStreaming进行流数据处理的大致流程可分为:启动流处理引擎接受和存储流数据处理流数据输出处理结果 四个步骤,其运行的架构图如下:
在这里插入图片描述

  1. 启动流引擎时,需要先初始化StreamingContext,在调用StreamingContext实例的start方法后,在该对象启动过程中实例化DStreamGraph和JobScheduler,其中DStreamGraph用于存放DStream之间的依赖关系。JobScheduler包含ReceiverTracker和JobGenerator。其中ReceiverTracker为Driver端流数据接收器Receiver的管理器,JobGenerator为批处理作业生产器。在ReceiverTracker的启动过程中,根据流数据接收器分发策略通知对应的Executor中的流数据接收器管理器ReceiverSupervisor启动,再由ReceiverSupervisor启动流数据的接收器。
  2. 流数据接收与存储。当流数据接收器Receiver启动后,会持续不断地接收实时流数据,流数据的存储会根据数据量大小进行判断。如果数据量很小,会攒成多条数据成一块,然后进行块存储,如果数据量大,则进行直接存储,这些数据由Receiver直接交给ReceiverSupervisor,由其进行数据转换存储操作。ReceivedBlockHandler接口对于块存储的存储根据是否设置预写日志分成两种的形式。第一种对于没有设置预写日志的写入形式,采用BlockManagerBasedBlockHandler方法直接写入Spark Worker中的内存或者磁盘中。第二种是设置了预写日志的写入形式,即调用WriteAheadLogBasedBlockHander方法,即在预写日志的同时将数据写入Worker的内存或者磁盘当中。数据存储完毕后,ReceiverSupervisor会把数据存储的元信息上报给ReceiverTracker,ReceiverTracker再把这些信息准发给ReceiverBlockTracker,由它负责管理接收数据的元信息。
  3. 流数据得处理。StreamingContext当中的JobGenerator中维护着一个定时器,该定时器在批处理时间到来时会生成作业的操作,主要进行以下操作:
     【1】通知ReceiverTracker将接受到的数据进行提交,提交的时候采用synchronized关键字进行处理,保证每条数据被划入一个且只能被划入一个批中
     【2】要求DStreamGraph根据DStream的依赖关系生产作业序列
     【3】从第一步中ReceiverTracker获取本批次数据的元数据
     【4】把批处理时间、作业序列和本批次数据的元数据包装为JobSet,调用JobScheduler.submitJobSet(JobSet)提交给JobScheduler,JobScheduler将把作业发送给Spark核心进行处理,由于该执行行为为异步,所以执行速度很快
     【5】只要提交结束,Spark Streaming对整个系统进行CheckPoint
  4. 流数据的输出操作。在Spark核心的作业对数据进行处理完毕后输出到外部系统

Spark Streaming、Flink和Storm的优势分析

1、Spark Streaming在吞吐量上要比Storm优秀
2、Storm在实时延迟度上,比Spark Streaming就好多了,前者是纯实时,后者是准实时。而且,Storm的事务机制、健壮性/容错性、动态调整并行度等特性,都要比Spark Streaming更加优秀
3、Spark Streaming,有一点是Storm绝对比不上的,就是:它位于Spark整个生态技术栈中,因此Spark Streaming可以和Spark Core、Spark SQL、Spark Graphx无缝整合,即可以对实时处理出来的中间数据,立即在程序中无缝进行延迟批处理、交互式查询等操作

Spark Streaming、Flink和Storm的应用场景
Storm
1、要求纯实时进行交易和分析时
2、在实时计算的功能中,要求可靠的事务机制和可靠性机制,即数据的处理完全精准,一条也不能多,一条也不能少
3、如果需要考虑针对高峰低峰时间段,动态调整实时计算程序的并行度,以最大限度利用集群资源

Spark Streaming
  考虑使用Spark Streaming最主要的一个因素,应该是针对整个项目进行宏观的考虑,即,如果一个项目除了实时计算之外,还包括了离线批处理、交互式查询、图计算和MLIB机器学习等业务功能,而且实时计算中,可能还会牵扯到高延迟批处理、交互式查询等功能,那么就应该首选Spark生态,用Spark Core开发离线批处理,用Spark SQL开发交互式查询,用Spark Streaming开发实时计算,三者可以无缝整合,给系统提供非常高的可扩展性

Flink
1.支持高吞吐、低延迟、高性能的流处理
2.支持带有事件时间的窗口(Window)操作
3.支持有状态计算的Exactly-once语义
4.支持高度灵活的窗口(Window)操作,支持基于time、count、session,以及data-driven的窗口操作
5.支持具有Backpressure功能的持续流模型
6.支持基于轻量级分布式快照(Snapshot)实现的容错
7.一个运行时同时支持Batch on Streaming处理和Streaming处理
8.Flink在JVM内部实现了自己的内存管理
9.支持迭代计算
10.支持程序自动优化:避免特定情况下Shuffle、排序等昂贵操作,中间结果有必要进行缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值