-
介绍
-
入门
-
原理
-
操作
1. Spark Streaming 介绍
-
流式计算的场景
-
流式计算框架
-
Spark Streaming的特点
-
新的场景
- 流计算
- 流和批的架构组合
Spark Streaming 的特点
2. Spark Streaming 入门
-
环境准备
-
工程搭建
-
代码编写
-
总结
- 创建工程
Netcat 的使用
def main(args: Array[String]): Unit = {
if (args.length < 2) {
System.err.println(“Usage: NetworkWordCount <hostname> <port>”)
System.exit(1)
}
<span class="hljs-keyword">val</span> sparkConf = <span class="hljs-keyword">new</span> <span class="hljs-type">SparkConf</span>().setAppName(<span class="hljs-string">"NetworkWordCount"</span>)
<span class="hljs-keyword">val</span> ssc = <span class="hljs-keyword">new</span> <span class="hljs-type">StreamingContext</span>(sparkConf, <span class="hljs-type">Seconds</span>(<span class="hljs-number">1</span>)) <i class="conum" data-value="1"></i><b>(<span class="hljs-number">1</span>)</b>
<span class="hljs-keyword">val</span> lines = ssc.socketTextStream( <i class="conum" data-value="2"></i><b>(<span class="hljs-number">2</span>)</b>
hostname = args(<span class="hljs-number">0</span>),
port = args(<span class="hljs-number">1</span>).toInt,
storageLevel = <span class="hljs-type">StorageLevel</span>.<span class="hljs-type">MEMORY_AND_DISK_SER</span>) <i class="conum" data-value="3"></i><b>(<span class="hljs-number">3</span>)</b>
<span class="hljs-keyword">val</span> words = lines.flatMap(_.split(<span class="hljs-string">" "</span>))
<span class="hljs-keyword">val</span> wordCounts = words.map(x => (x, <span class="hljs-number">1</span>)).reduceByKey(_ + _)
wordCounts.print() <i class="conum" data-value="4"></i><b>(<span class="hljs-number">4</span>)</b>
ssc.start() <i class="conum" data-value="5"></i><b>(<span class="hljs-number">5</span>)</b>
ssc.awaitTermination() <i class="conum" data-value="6"></i><b>(<span class="hljs-number">6</span>)</b>
}
}
| 1 | 在 Spark 中, 一般使用 XXContext 来作为入口, Streaming 也不例外, 所以创建 StreamingContext 就是创建入口 |
| 2 | 开启 Socket 的 Receiver, 连接到某个 TCP 端口, 作为 Socket client, 去获取数据 |
| 3 | 选择 Receiver 获取到数据后的保存方式, 此处是内存和磁盘都有, 并且序列化后保存 |
| 4 | 类似 RDD 中的 Action, 执行最后的数据输出和收集 |
| 5 | 启动流和 JobGenerator, 开始流式处理数据 |
| 6 | 阻塞主线程, 后台线程开始不断获取数据并处理 |
Step 4: 部署和上线
-
使用 Maven 命令 package 打包
-
将打好的包上传到
node01
-
在
node02上使用nc开启一个Socket server, 接受Streaming程序的连接请求, 从而建立连接发送消息给Streaming程序实时处理nc -lk 9999
-
在
node01执行如下命令运行程序spark-submit --class cn.itcast.streaming.StreamingWordCount --master local[6] original-streaming-0.0.1.jar node02 9999
Step 5: 总结和知识落地
2. 原理
-
总章
-
静态
DAG -
动态切分
-
数据流入
-
容错机制
-
总章
val words: DStream[String] = lines
.flatMap(.split(" "))
.map(x => (x, 1))
.reduceByKey( + _)
可以看到
-
RDD中针对数据的处理是使用算子, 在DStream中针对数据的操作也是算子 -
DStream的算子似乎和RDD没什么区别
有一个疑惑
-
难道
DStream会把算子的操作交给RDD去处理? 如何交?
Spark Streaming 是流计算, 流计算的数据是无限的
什么系统可以产生无限的数据?
无限的数据一般指的是数据不断的产生, 比如说运行中的系统, 无法判定什么时候公司会倒闭, 所以也无法断定数据什么时候会不再产生数据
-
那就会产生一个问题
-
如何不简单的读取数据, 如何应对数据量时大时小?
如何数据是无限的, 意味着可能要一直运行下去
-
那就会又产生一个问题
-
Spark Streaming不会出错吗? 数据出错了怎么办?
总结下来, 有四个问题
-
DStream如何对应RDD? -
如何切分
RDD? -
如何读取数据?
-
如何容错?
DAG 的定义
3. 操作
这一小节主要目的是为了了解 Spark Streaming 一些特别特殊和重要的操作, 一些基本操作基本类似 RDD
updateStateByKey
val lines: DStream[String] = ssc.socketTextStream(
hostname = “localhost”,
port = “9999”.toInt,
storageLevel = StorageLevel.MEMORY_AND_DISK_SER)
val words = lines.flatMap(_.split(" ")).map(x => (x, 1))
// 使用 updateStateByKey 必须设置 Checkpoint 目录
ssc.checkpoint(“checkpoint”)
// updateStateByKey 的函数
def updateFunc(newValue: Seq[Int], runningValue: Option[Int]) = {
// newValue 之所以是一个 Seq, 是因为它是某一个 Batch 的某个 Key 的全部 Value
val currentBatchSum = newValue.sum
val state = runningValue.getOrElse(0)
// 返回的这个 Some(count) 会再次进入 Checkpoint 中当作状态存储
Some(currentBatchSum + state)
}
// 调用
val wordCounts = words.updateStateByKeyInt
wordCounts.print()
ssc.start()
ssc.awaitTermination()
window 操作
val lines: DStream[String] = ssc.socketTextStream(
hostname = “localhost”,
port = 9999,
storageLevel = StorageLevel.MEMORY_AND_DISK_SER)
val words = lines.flatMap(_.split(" ")).map(x => (x, 1))
// 通过 window 操作, 会将流分为多个窗口
val wordsWindow = words.window(Seconds(30), Seconds(10))
// 此时是针对于窗口求聚合
val wordCounts = wordsWindow.reduceByKey((newValue, runningValue) => newValue + runningValue)
wordCounts.print()
ssc.start()
ssc.awaitTermination()
-
既然
window操作经常配合reduce这种聚合, 所以Spark Streaming提供了较为方便的方法
val sparkConf = new SparkConf().setAppName("NetworkWordCount").setMaster("local[6]")
val sc = new SparkContext(sparkConf)
sc.setLogLevel("ERROR")
val ssc = new StreamingContext(sc, Seconds(1))
val lines: DStream[String] = ssc.socketTextStream(
hostname = “localhost”,
port = 9999,
storageLevel = StorageLevel.MEMORY_AND_DISK_SER)
val words = lines.flatMap(_.split(" ")).map(x => (x, 1))
// 开启窗口并自动进行 reduceByKey 的聚合
val wordCounts = words.reduceByKeyAndWindow(
reduceFunc = (n, r) => n + r,
windowDuration = Seconds(30),
slideDuration = Seconds(10))
wordCounts.print()
ssc.start()
ssc.awaitTermination()
-
在
window函数中, 接收两个参数-
windowDuration窗口长度,window函数会将多个DStream中的RDD按照时间合并为一个, 那么窗口长度配置的就是将多长时间内的RDD合并为一个 -
slideDuration滑动间隔, 比较好理解的情况是直接按照某个时间来均匀的划分为多个window, 但是往往需求可能是统计最近xx分内的所有数据, 一秒刷新一次, 那么就需要设置滑动窗口的时间间隔了, 每隔多久生成一个window
-
-
滑动时间的问题
-
如果
windowDuration > slideDuration, 则在每一个不同的窗口中, 可能计算了重复的数据 -
如果
windowDuration < slideDuration, 则在每一个不同的窗口之间, 有一些数据为能计算进去
但是其实无论谁比谁大, 都不能算错, 例如, 我的需求有可能就是统计一小时内的数据, 一天刷新两次
-
本文深入探讨Spark Streaming的核心概念,包括微批处理模型、DStream API、容错机制及实时数据处理流程。通过实战案例,如网络词频统计,详解如何搭建Spark Streaming应用程序,并介绍关键操作如updateStateByKey和window函数的应用。
387

被折叠的 条评论
为什么被折叠?



