Spark(四)-- SparkStreaming原理 (二)

目录

1.总章

1.1 Spark Streaming 的特点

1.2 Spark Streaming 是按照时间切分小批量

1.2.1 如何小批量?

1.2.2 如何处理数据?

1.3 Spark Streaming 是流计算, 流计算的数据是无限的

1.4 总结

2.DAG 的定义

2.1 RDD 和 DStream 的 DAG(有向无环图)

2.2 RDD 和 DStream 的区别

2.3 DStream 如何形式 DAG

3. 切分流, 生成小批量

3.1 静态和动态

3.2 动态生成 RDD DAG 的过程

4. 数据的产生和导入

4.1 Receiver

4.2 Receiver 的结构

4.3 Receiver 的执行过程

5. 容错

5.1 热备

5.2 冷备

5.3 重放

6.学习笔记

6.1 SparkStreaming 原理

6.2 执行图

6.3 详细图​


本节主要内容:

  1. 总章

  2. 静态 DAG

  3. 动态切分

  4. 数据流入

  5. 容错机制

1.总章

1.1 Spark Streaming 的特点

  • Spark Streaming 会源源不断的处理数据, 称之为流计算

  • Spark Streaming 并不是实时流, 而是按照时间切分小批量, 一个一个的小批量处理

  • Spark Streaming 是流计算, 所以可以理解为数据会源源不断的来, 需要长时间运行

1.2 Spark Streaming 是按照时间切分小批量

1.2.1 如何小批量?

Spark Streaming 中的编程模型叫做 DStream, 所有的 API 都从 DStream 开始, 其作用就类似于 RDD 之于 Spark Core

可以理解为 DStream 是一个管道, 数据源源不断的从这个管道进去, 被处理, 再出去

但是需要注意的是, DStream 并不是严格意义上的实时流, 事实上, DStream 并不处理数据, 而是处理 RDD

以上, 可以整理出如下道理

  • Spark Streaming 是小批量处理数据, 并不是实时流

  • Spark Streaming 对数据的处理是按照时间切分为一个又一个小的 RDD, 然后针对 RDD 进行处理

所以针对以上的解读, 可能会产生一种疑惑

  • 如何切分 RDD?

1.2.2 如何处理数据?

如下代码

val lines: DStream[String] = ssc.socketTextStream(
  hostname = args(0),
  port = args(1).toInt,
  storageLevel = StorageLevel.MEMORY_AND_DISK_SER)

val words: DStream[String] = lines
  .flatMap(_.split(" "))
  .map(x => (x, 1))
  .reduceByKey(_ + _)

可以看到

  • RDD 中针对数据的处理是使用算子, 在 DStream 中针对数据的操作也是算子

  • DStream 的算子似乎和 RDD 没什么区别

有一个疑惑

  • 难道 DStream 会把算子的操作交给 RDD 去处理? 如何交?

1.3 Spark Streaming 是流计算, 流计算的数据是无限的

 什么系统可以产生无限的数据?

无限的数据一般指的是数据不断的产生, 比如说运行中的系统, 无法判定什么时候公司会倒闭, 所以也无法断定数据什么时候会不再产生数据

那就会产生一个问题

如何不简单的读取数据, 如何应对数据量时大时小?

如何数据是无限的, 意味着可能要一直运行下去

那就会又产生一个问题

Spark Streaming 不会出错吗? 数据出错了怎么办?

1.4 总结

总结下来, 有四个问题

  • DStream 如何对应 RDD?

  • 如何切分 RDD?

  • 如何读取数据?

  • 如何容错?

2.DAG 的定义

2.1 RDD 和 DStream 的 DAG(有向无环图)

如果是 RDD 的 WordCount, 代码大致如下

val textRDD = sc.textFile(...)
val splitRDD = textRDD.flatMap(_.split(" "))
val tupleRDD = splitRDD.map((_, 1))
val reduceRDD = tupleRDD.reduceByKey(_ + _)

用图形表示如下

同样, DStream 的代码大致如下

val lines: DStream[String] = ssc.socketTextStream(...)
val words: DStream[String] = lines.flatMap(_.split(" "))
val wordCounts: DStream[(String, Int)] = words.map(x => (x, 1)).reduceByKey(_ + _)

同理, DStream 也可以形成 DAG 如下

看起来 DStream 和 RDD 好像哟, 确实如此

2.2 RDD 和 DStream 的区别

  • DStream 的数据是不断进入的, RDD 是针对一个数据的操作

  • 像 RDD 一样, DStream 也有不同的子类, 通过不同的算子生成

  • 一个 DStream 代表一个数据集, 其中包含了针对于上一个数据的操作

  • DStream 根据时间切片, 划分为多个 RDD, 针对 DStream 的计算函数, 会作用于每一个 DStream 中的 RDD

2.3 DStream 如何形式 DAG

  • 每个 DStream 都有一个关联的 DStreamGraph 对象

  • DStreamGraph 负责表示 DStream 之间的的依赖关系和运行步骤

  • DStreamGraph 中会单独记录 InputDStream 和 OutputDStream

3. 切分流, 生成小批量

3.1 静态和动态

根据前面的学习, 可以总结一下规律

  • DStream 对应 RDD

  • DStreamGraph 表示 DStream 之间的依赖关系和运行流程, 相当于 RDD 通过 DAGScheduler 所生成的 RDD DAG

但是回顾前面的内容, RDD 的运行分为逻辑计划和物理计划

  • 逻辑计划就是 RDD 之间依赖关系所构成的一张有向无环图

  • 后根据这张 DAG 生成对应的 TaskSet 调度到集群中运行, 如下

但是在 DStream 中则不能这么简单的划分, 因为 DStream 中有一个非常重要的逻辑, 需要按照时间片划分小批量

  • 在 Streaming 中, DStream 类似 RDD, 生成的是静态的数据处理过程, 例如一个 DStream 中的数据经过 map 转为其它模样

  • 在 Streaming 中, DStreamGraph 类似 DAG, 保存了这种数据处理的过程

上述两点, 其实描述的是静态的一张 DAG, 数据处理过程, 但是 Streaming 是动态的, 数据是源源不断的来的

所以, 在 DStream 中, 静态和动态是两个概念, 有不同的流程

  • DStreamGraph 将 DStream 联合起来, 生成 DStream 之间的 DAG, 这些 DStream 之间的关系是相互依赖的关系, 例如一个 DStream 经过 map 转为另外一个 DStream

  • 但是把视角移动到 DStream 中来看, DStream 代表了源源不断的 RDD 的生成和处理, 按照时间切片, 所以一个 DStream DAG 又对应了随着时间的推进所产生的无限个 RDD DAG

3.2 动态生成 RDD DAG 的过程

RDD DAG 的生成是按照时间来切片的, Streaming 会维护一个 Timer, 固定的时间到达后通过如下五个步骤生成一个 RDD DAG 后调度执行

  1. 通知 Receiver 将收到的数据暂存, 并汇报存储的元信息, 例如存在哪, 存了什么

  2. 通过 DStreamGraph 复制出一套新的 RDD DAG

  3. 将数据暂存的元信息和 RDD DAG 一同交由 JobScheduler 去调度执行

  4. 提交结束后, 对系统当前的状态 Checkpoint

4. 数据的产生和导入

4.1 Receiver

在 Spark Streaming 中一个非常大的挑战是, 很多外部的队列和存储系统都是分块的, RDD 是分区的, 在读取外部数据源的时候, 会用不同的分区对照外部系统的分片, 例如

不仅 RDDDStream 中也面临这种挑战

那么此处就有一个小问题

  • DStream 中是 RDD 流, 只是 RDD 的分区对应了 Kafka 的分区就可以了吗?

答案是不行, 因为需要一套单独的机制来保证并行的读取外部数据源, 这套机制叫做 Receiver

4.2 Receiver 的结构

为了保证并行获取数据, 对应每一个外部数据源的分区, 所以 Receiver 也要是分布式的, 主要分为三个部分

  • Receiver 是一个对象, 是可以有用户自定义的获取逻辑对象, 表示了如何获取数据

  • Receiver Tracker 是 Receiver 的协调和调度者, 其运行在 Driver 上

  • Receiver Supervisor 被 Receiver Tracker 调度到不同的几点上分布式运行, 其会拿到用户自定义的 Receiver 对象, 使用这个对象来获取外部数据

4.3 Receiver 的执行过程

  1. 在 Spark Streaming 程序开启时候, Receiver Tracker 使用 JobScheduler 分发 Job 到不同的节点, 每个 Job 包含一个 Task , 这个 Task 就是 Receiver Supervisor, 这个部分的源码还挺精彩的, 其实是复用了通用的调度逻辑

  2. ReceiverSupervisor 启动后运行 Receiver 实例

  3. Receiver 启动后, 就将持续不断地接收外界数据, 并持续交给 ReceiverSupervisor 进行数据存储

  4. ReceiverSupervisor 持续不断地接收到 Receiver 转来的数据, 并通过 BlockManager 来存储数据

  5. 获取的数据存储完成后发送元数据给 Driver 端的 ReceiverTracker, 包含数据块的 id, 位置, 数量, 大小 等信息

5. 容错

因为要非常长时间的运行, 对于任何一个流计算系统来说, 容错都是非常致命也非常重要的一环, 在 Spark Streaming 中, 大致提供了如下的容错手段

5.1 热备

还记得这行代码吗

这行代码中的 StorageLevel.MEMORY_AND_DISK_SER 的作用是什么? 其实就是热备份

  • 当 Receiver 获取到数据要存储的时候, 是交给 BlockManager 存储的

  • 如果设置了 StorageLevel.MEMORY_AND_DISK_SER, 则意味着 BlockManager 不仅会在本机存储, 也会发往其它的主机进行存储, 本质就是冗余备份

  • 如果某一个计算失败了, 通过冗余的备份, 再次进行计算即可

这是默认的容错手段

5.2 冷备

冷备在 Spark Streaming 中的手段叫做 WAL (预写日志)

  • 当 Receiver 获取到数据后, 会交给 BlockManager 存储

  • 在存储之前先写到 WAL 中, WAL 中保存了 Redo Log, 其实就是记录了数据怎么产生的, 以便于恢复的时候通过 Log恢复

  • 当出错的时候, 通过 Redo Log 去重放数据

5.3 重放

  • 有一些上游的外部系统是支持重放的, 比如说 Kafka

  • Kafka 可以根据 Offset 来获取数据

  • 当 SparkStreaming 处理过程中出错了, 只需要通过 Kafka 再次读取即可

6.学习笔记

6.1 SparkStreaming 原理

(1)DStream内部是由RDD组成的
(2)DStream本身也是可以组成DAG(有向无环图)的,因为DStream通过一些转换算子,也是生成了新的DStream

6.2 执行图

RDD:    map -> flatMap -> reduceByKey

每一个RDD有自己的有向无环图,最终DStream会通过DStreamGraph构建自己的有向无环图(我的理解:其实DSteam的有向无环图就是RDD的有向无环图)

DStream本身没有意义,有意义的在于其内部的RDD

6.3 详细图

可以看出每一个RDD的计算顺序都是对应的

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值