7.6 转换操作
一、DStream无状态转换操作
无状态转换操作:之前“套接字流”部分介绍的词频统计,就是采用无状态转换,每次统计,都是只统计当前批次到达的单词的词频,和之前批次无关,不会进行累计。
1.map(func)
对源DStream的每个元素,采用func函数进行转换,得到一个新的Dstream。
2.flatMap(func)
与map相似,但是每个输入项可用被映射为0个或者多个输出项。
3.filter(func)
返回一个新的DStream,仅包含源DStream中满足函数func的项。
4.repartition(numPartitions)
通过创建更多或者更少的分区改变DStream的并行程度。
5.reduce(func)
利用函数func聚集源DStream中每个RDD的元素,返回一个包含单元素RDDs的新DStream。
6.count()
统计源DStream中每个RDD的元素数量。
7.union(otherStream)
返回一个新的DStream,包含源DStream和其他DStream的元素。
8.countByValue()
应用于元素类型为K的DStream上,返回一个(K,V)键值对类型的新DStream,每个键的值是在原DStream的每个RDD中的出现次数。
9.reduceByKey(func, [numTasks])
当在一个由(K,V)键值对组成的DStream上执行该操作时,返回一个新的由(K,V)键值对组成的DStream,每一个key的值均由给定的recuce函数(func)聚集起来。
10.join(otherStream, [numTasks])
当应用于两个DStream(一个包含(K,V)键值对,一个包含(K,W)键值对),返回一个包含(K, (V, W))键值对的新Dstream。
11.cogroup(otherStream, [numTasks])
当应用于两个DStream(一个包含(K,V)键值对,一个包含(K,W)键值对),返回一个包含(K, Seq[V], Seq[W])的元组。
12.transform(func)
通过对源DStream的每个RDD应用RDD-to-RDD函数,创建一个新的DStream。支持在新的DStream中做任何RDD操作。
二、DStream有状态转换操作
1.滑动窗口转换操作
- 事先设定一个滑动窗口的长度(也就是窗口的持续时间)
- 设定滑动窗口的时间间隔(每隔多长时间执行一次计算),让窗口按照指定时间间隔在源DStream上滑动
- 每次窗口停放的位置上,都会有一部分Dstream(或者一部分RDD)被框入窗口内,形成一个小段的Dstream
- 可以启动对这个小段DStream的计算。
一些窗口转换操作的含义:
(1)window(windowLength, slideInterval) 基于源DStream产生的窗口化的批数据,计算得到一个新的Dstream;
(2)countByWindow(windowLength, slideInterval) 返回流中元素的一个滑动窗口数;
(3)reduceByWindow(func, windowLength, slideInterval) 返回一个单元素流。利用函数func聚集滑动时间间隔的流的元素创建这个单元素流。函数func必须满足结合律,从而可以支持并行计算;
(4)reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 应用到一个(K,V)键值对组成的DStream上时,会返回一个由(K,V)键值对组成的新的DStream。每一个key的值均由给定的reduce函数(func函数)进行聚合计算。注意:在默认情况下,这个算子利用了Spark默认的并发任务数去分组。可以通过numTasks参数的设置来指定不同的任务数。
(5)reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每个窗口的reduce值,是基于先前窗口的reduce值进行增量计算得到的;它会对进入滑动窗口的新数据进行reduce操作,并对离开窗口的老数据进行“逆向reduce”操作。但是,只能用于“可逆reduce函数”,即那些reduce函数都有一个对应的“逆向reduce函数”(以InvFunc参数传入);
窗口转换操作实例:
在上一节的“Apache Kafka作为DStream数据源”内容中,已经使用了窗口转换操作,在KafkaWordCount.scala代码中,可以找到下面这一行:
1 | val wordCounts = pair.reduceByKeyAndWindow(_ + _,_ - _,Minutes( 2 ),Seconds( 10 ), 2 ) |
(6)countByValueAndWindow(windowLength, slideInterval, [numTasks]) 当应用到一个(K,V)键值对组成的DStream上,返回一个由(K,V)键值对组成的新的DStream。每个key的值都是它们在滑动窗口中出现的频率。
2.updateStateByKey操作
需要在跨批次之间维护状态时,就必须使用updateStateByKey操作。
词频统计实例:
对于有状态转换操作而言,本批次的词频统计,会在之前批次的词频统计结果的基础上进行不断累加,所以,最终统计得到的词频,是所有批次的单词的总的词频统计结果。
新建一个NetworkWordCountStateful.scala代码文件,输入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package org.apache.spark.examples.streaming import org.apache.spark._ import org.apache.spark.streaming._ import org.apache.spark.storage.StorageLevel object NetworkWordCountStateful { def main(args: Array[String]) { / / 定义状态更新函数 val updateFunc = (values: Seq[ Int ], state: Option[ Int ]) = > { # state是维护当前历史状态的词频 val currentCount = values.foldLeft( 0 )(_ + _) val previousCount = state.getOrElse( 0 ) Some(currentCount + previousCount) } StreamingExamples.setStreamingLogLevels() / / 设置log4j日志级别 val conf = new SparkConf().setMaster( "local[2]" ).setAppName( "NetworkWordCountStateful" ) val sc = new StreamingContext(conf, Seconds( 5 )) sc.checkpoint( "file:///usr/local/spark/mycode/streaming/stateful/" ) / / 设置检查点,检查点具有容错机制 val lines = sc.socketTextStream( "localhost" , 9999 ) val words = lines.flatMap(_.split( " " )) val wordDstream = words. map (x = > (x, 1 )) val stateDstream = wordDstream.updateStateByKey[ Int ](updateFunc) stateDstream. print () sc.start() sc.awaitTermination() } } |
新建一个StreamingExamples.scala文件,用于设置log4j日志级别,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package org.apache.spark.examples.streaming import org.apache.spark.Logging import org.apache.log4j.{Level, Logger} / * * Utility functions for Spark Streaming examples. * / object StreamingExamples extends Logging { / * * Set reasonable logging levels for streaming if the user has not configured log4j. * / def setStreamingLogLevels() { val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements if (!log4jInitialized) { / / We first log something to initialize Spark's default logging, then we override the / / logging level. logInfo( "Setting log level to [WARN] for streaming example." + " To override add a custom log4j.properties to the classpath." ) Logger.getRootLogger.setLevel(Level.WARN) } } } |
创建simple.sbt文件:
1 2 3 4 | name : = "Simple Project" version : = "1.0" scalaVersion : = "2.11.8" libraryDependencies + = "org.apache.spark" % % "spark-streaming" % "2.1.0" |
执行sbt打包编译,输入以下命令启动这个程序:
执行上面命令后,就进入了监听状态(称为监听窗口)
新打开一个窗口作为nc窗口,启动nc程序:
切换到刚才的监听窗口,会发现,已经输出了词频统计信息:
参考文献:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现