Spark入门:DStream转换操作

DStream转换操作包括无状态转换和有状态转换。

  • 无状态转换:每个批次的处理不依赖于之前批次的数据。
  • 有状态转换:当前批次的处理需要使用之前批次的数据或者中间结果。有状态转换包括基于滑动窗口的转换和追踪状态变化的转换(updateStateByKey)。

一、DStream无状态转换操作

下面给出一些无状态转换操作的含义:
* map(func) :对源DStream的每个元素,采用func函数进行转换,得到一个新的DStream;
* flatMap(func): 与map相似,但是每个输入项可用被映射为0个或者多个输出项;
* filter(func): 返回一个新的DStream,仅包含源DStream中满足函数func的项;
* repartition(numPartitions): 通过创建更多或者更少的分区改变DStream的并行程度;
* union(otherStream): 返回一个新的DStream,包含源DStream和其他DStream的元素;
* count():统计源DStream中每个RDD的元素数量;
* reduce(func):利用函数func聚集源DStream中每个RDD的元素,返回一个包含单元素RDDs的新DStream;
* countByValue():应用于元素类型为K的DStream上,返回一个(K,V)键值对类型的新DStream,每个键的值是在原DStream的每个RDD中的出现次数;
* reduceByKey(func, [numTasks]):当在一个由(K,V)键值对组成的DStream上执行该操作时,返回一个新的由(K,V)键值对组成的DStream,每一个key的值均由给定的recuce函数(func)聚集起来;
* join(otherStream, [numTasks]):当应用于两个DStream(一个包含(K,V)键值对,一个包含(K,W)键值对),返回一个包含(K, (V, W))键值对的新DStream;
* cogroup(otherStream, [numTasks]):当应用于两个DStream(一个包含(K,V)键值对,一个包含(K,W)键值对),返回一个包含(K, Seq[V], Seq[W])的元组;
* transform(func):通过对源DStream的每个RDD应用RDD-to-RDD函数,创建一个新的DStream。支持在新的DStream中做任何RDD操作。

无状态转换操作实例:我们之前“套接字流”部分介绍的词频统计,就是采用无状态转换,每次统计,都是只统计当前批次到达的单词的词频,和之前批次无关,不会进行累计。

二、DStream有状态转换操作

        对于DStream有状态转换操作而言,当前批次的处理需要使用之前批次的数据或者中间结果。有状态转换包括基于滑动窗口的转换和追踪状态变化(updateStateByKey)的转换

三、滑动窗口转换操作

        滑动窗口转换操作的计算过程如下图所示,我们可以事先设定一个滑动窗口的长度(也就是窗口的持续时间),并且设定滑动窗口的时间间隔(每隔多长时间执行一次计算),然后,就可以让窗口按照指定时间间隔在源DStream上滑动,每次窗口停放的位置上,都会有一部分DStream被框入窗口内,形成一个小段的DStream,这时,就可以启动对这个小段DStream的计算。

下面给给出一些窗口转换操作的含义:
* window(windowLength, slideInterval) 基于源DStream产生的窗口化的批数据,计算得到一个新的DStream;
* countByWindow(windowLength, slideInterval) 返回流中元素的一个滑动窗口数;
* reduceByWindow(func, windowLength, slideInterval) 返回一个单元素流。利用函数func聚集滑动时间间隔的流的元素创建这个单元素流。函数func必须满足结合律,从而可以支持并行计算;
* reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 应用到一个(K,V)键值对组成的DStream上时,会返回一个由(K,V)键值对组成的新的DStream。每一个key的值均由给定的reduce函数(func函数)进行聚合计算。注意:在默认情况下,这个算子利用了Spark默认的并发任务数去分组。可以通过numTasks参数的设置来指定不同的任务数;
* reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每个窗口的reduce值,是基于先前窗口的reduce值进行增量计算得到的;它会对进入滑动窗口的新数据进行reduce操作,并对离开窗口的老数据进行“逆向reduce”操作。但是,只能用于“可逆reduce函数”,即那些reduce函数都有一个对应的“逆向reduce函数”(以InvFunc参数传入);
* countByValueAndWindow(windowLength, slideInterval, [numTasks]) 当应用到一个(K,V)键值对组成的DStream上,返回一个由(K,V)键值对组成的新的DStream。每个key的值都是它们在滑动窗口中出现的频率。

四、updateStateByKey操作

        下面我们就给出一个具体实例。我们还是以前面在“套接字流”部分讲过的NetworkWordCount为例子来介绍,在之前的套接字流的介绍中,我们统计单词词频采用的是无状态转换操作,也就是说,每个批次的单词发送给NetworkWordCount程序处理时,NetworkWordCount只对本批次内的单词进行词频统计,不会考虑之前到达的批次的单词,所以,不同批次的单词词频都是独立统计的。
        对于有状态转换操作而言,本批次的词频统计,会在之前批次的词频统计结果的基础上进行不断累加,所以,最终统计得到的词频,是所有批次的单词的总的词频统计结果。

1.登录Linux系统,打开一个终端,然后,执行下面命令
cd ~
mkdir stateful
mkdir -p ./stateful/src/main/scala
vim ./stateful/src/main/scala/NetworkWordCountStateful.scala
2.上面使用vim编辑器新建了一个NetworkWordCountStateful.scala代码文件,请在里面输入以下代码:
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]) => {
      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:///home/qiangzi/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()
  }
}
3.然后,再次使用vim编辑器新建一个StreamingExamples.scala文件
cd ~
vim ./stateful/src/main/scala/StreamingExamples.scala
4.StreamingExamples.scala文件,用于设置log4j日志级别,代码如下:
import org.apache.log4j.{Level, Logger}

/** Utility functions for Spark Streaming examples. */
object StreamingExamples {
  /** Set reasonable logging levels for streaming if the user has not configured log4j. */
  def setStreamingLogLevels(): Unit = {
    val rootLogger = Logger.getRootLogger
    if (!rootLogger.getAllAppenders.hasMoreElements) {
      // We first log something to initialize Spark's default logging, then we override the
      // logging level.
      rootLogger.info("Setting log level to [WARN] for streaming example." +
        " To override add a custom log4j.properties to the classpath.")
      rootLogger.setLevel(Level.WARN)
    }
  }
}
5.退出vim编辑器。下面要对代码进行sbt打包编译。这里需要一个simple.sbt文件,使用vim编辑器创建一个:
cd ./stateful
vim simple.sbt
6.在simple.sbt中输入以下内容:
name := "Simple Project"
version := "1.0"
scalaVersion := "2.12.18"
libraryDependencies += "org.apache.spark" %% "spark-streaming" % "3.5.1"
7.创建好simple.sbt文件后,退出vim编辑器。然后,执行下面命令:
cd ./stateful
find .
8.屏幕上返回的信息,应该是类似下面的文件结构:

9.然后,就可以执行sbt打包编译了,命令如下:
cd ./stateful
/usr/local/sbt-1.9.0/sbt/sbt package
10.打包成功结果

11.打包成功以后,就可以输入以下命令启动这个程序:
cd ./stateful
/usr/local/spark-3.5.1/bin/spark-submit --class "NetworkWordCountStateful" ~/stateful/target/scala-2.12/simple-project_2.12-1.0.jar
12.执行上面命令后,就进入了监听状态(我们把运行这个监听程序的窗口称为监听窗口),这时,你就可以像刚才一样,新打开一个窗口作为nc窗口,启动nc程序:

13.然后,你切换到刚才的监听窗口,会发现,已经输出了词频统计信息:

以上内容是使用最新发行的Spark版本,代码有所修改,其他版本可参考学习资源链接:Spark入门:DStream转换操作_厦大数据库实验室博客

  • 16
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,关于 Spark2.1.0 中的套接字流(DStream),我可以给你一些简单的介绍和入门指导。 DStreamSpark Streaming 中的核心概念,它代表了一个连续的数据流,可以由一个或多个输入源(如 Kafka、Flume、HDFS 等)产生。在 Spark Streaming 中,DStream 是以微批处理(micro-batch processing)的方式进行处理的,即将一段时间内的数据作为一个批次进行处理,这个时间段可以通过设置参数来指定。 对于套接字流(Socket DStream),它是通过监听一个 TCP/IP 端口来接收数据的。在 Spark Streaming 中,创建 Socket DStream 的方式如下: ```scala import org.apache.spark.streaming.StreamingContext import org.apache.spark.streaming.Seconds val ssc = new StreamingContext(sparkConf, Seconds(batchInterval)) val lines = ssc.socketTextStream(hostname, port) ``` 其中,`sparkConf` 是 Spark 配置对象,`batchInterval` 表示数据批次的时间间隔,`hostname` 和 `port` 则表示监听的主机名和端口号。通过以上代码,就可以创建一个可以接收 Socket 数据的 DStream。 对于 Socket DStream,还可以进行一些操作,例如对每个批次的数据进行处理,可以使用 `map`、`filter`、`flatMap` 等函数;可以对多个 DStream 进行操作,例如使用 `union` 函数将两个 DStream 进行合并;还可以对 DStream 进行窗口(window)操作,例如使用 `window` 函数将多个连续的批次数据组成一个窗口,然后对窗口内的数据进行处理。 以上是关于 Spark2.1.0 中套接字流(DStream)的简单介绍和入门指导,希望能对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马龙强_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值