Spark Streaming状态管理函数(二)——updateStateByKey的使用(scala版)

updateStateByKey的使用

  关于updateStateByKey

  注意事项

  示例代码

  运行

  结论

关于updateStateByKey

  1.重点:首先会以DStream中的数据进行按key做reduce操作,然后再对各个批次的数据进行累加 。

  2.updateStateBykey要求必须要设置checkpoint点。

  3.updateStateByKey 方法中 updateFunc就要传入的参数,。Seq[V]表示当前key对应的所有值,Option[S] 是当前key的历史状态,返回的是新的封装的数据。

注意事项

  下面程序是使用idea编写的,使用的是scala语言,在程序中master(“local[2]”)设置为本地模式([]中的数指定的是线程数,不能少于2,否则看不到结果。主要是因为spark需要启动一个线程receiver来循环接收数据,一个Executor来接收数据,如果少于2线程不够将不能打印出结果。),在window上运行的。使用的spark版本是2.3.0,在2.x以后的版本,基本采用SparkSession来进行操作。同时,想要运行程序你的服务器上还必须要安装netcat这个软件,使用yum install nc进行安装(注意安全配置好yum源,DNS才能下载安装),使用命令nc -lk 6666开启服务发送数据。最后在运行程序前还需要导入spark、scala相应的依赖包。

示例代码
package spark2x

import org.apache.spark.SparkContext
import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
/**
  * 类名  UpdateStateByKeyDemo
  * 作者   彭三青
  * 创建时间  2018-12-01 9:35
  * 版本  1.0
  * 描述: $
  */

object UpdateStateByKeyDemo {
  def main(args: Array[String]): Unit = {
    /** 第一步:配置SparkConf:
      * 1,至少2条线程:因为Spark Streaming应用程序在运行的时候,至少有一条
      * 线程Receiver用于不断的循环接收数据,还有一条线程是Executor用于处理接受的数据(少于两条
      * 就没有线程用于处理数据,窗口不会显示数据。并且随着时间的推移,内存和磁盘由于负担过重而崩溃);
      * 2,对于集群而言,根据已有经验,大概5个左右的Core是性能最佳(一般分配为奇数个Core)
      */
     val spark = SparkSession.builder()
      .master("local[2]")
      .appName("UpdateStateByKeyDemo")
      .getOrCreate()
    val conf: SparkContext = spark.sparkContext
    /**
      * 第二步:创建SparkStreamingContext:
      * 1,这个是SparkStreaming应用程序所有功能的起始点和程序调度的核心
      * SparkStreamingContext的构建可以基于SparkConf参数,也可基于持久化的SparkStreamingContext的内容
      * 来恢复过来(典型的场景是Driver崩溃后重新启动,由于Spark Streaming具有连续7*24小时不间断运行的特征,
      * 所有需要在Driver重新启动后继续上衣系的状态,此时的状态恢复需要基于曾经的Checkpoint);
      * 2,在一个Spark Streaming应用程序中可以创建若干个SparkStreamingContext对象,使用下一个SparkStreamingContext
      * 之前需要把前面正在运行的SparkStreamingContext对象关闭掉,由此,我们获得一个重大的启发SparkStreaming框架也只是
      * Spark Core上的一个应用程序而已,只不过Spark Streaming框架箱运行的话需要Spark工程师写业务逻辑处理代码;
      */
    val ssc: StreamingContext = new StreamingContext(conf, Seconds(3))
    //报错解决办法做checkpoint,开启checkpoint机制,把checkpoint中的数据放在这里设置的目录中,生产环境下一般放在HDFS中
    ssc.checkpoint("hdfs://SC01:8020/user/tmp/cp-20181201")

    /**
      * 第三步:创建Spark Streaming输入数据来源input Stream:
      * 1,数据输入来源可以基于File、HDFS、Flume、Kafka、Socket等
      * 2, 在这里我们指定数据来源于网络Socket端口,Spark Streaming连接上该端口并在运行的时候一直监听该端口
      * 的数据(当然该端口服务首先必须存在),并且在后续会根据业务需要不断的有数据产生(当然对于Spark Streaming
      * 应用程序的运行而言,有无数据其处理流程都是一样的);
      * 3,如果经常在每间隔5秒钟没有数据的话不断的启动空的Job其实是会造成调度资源的浪费,因为并没有数据需要发生计算,所以
      * 实例的企业级生成环境的代码在具体提交Job前会判断是否有数据,如果没有的话就不再提交Job;
      */
    val line: ReceiverInputDStream[String] = ssc.socketTextStream("SC01", 6666)

    /**
      * 第四步:接下来就像对于RDD编程一样基于DStream进行编程!!!原因是DStream是RDD产生的模板(或者说类),在Spark Streaming具体
      * 发生计算前,其实质是把每个Batch的DStream的操作翻译成为对RDD的操作!!!
      * 对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
      * 进行单词拆分
      */
    val words: DStream[String] = line.flatMap(_.split(" "))

    /**
      * 对初始的DStream进行Transformation级别的处理,例如map、filter等高阶函数等的编程,来进行具体的数据计算
      * 单词分组计数实,word => (word, 1) Word ->(word, 1) day -> day(day, 1)
      */
    val pairs: DStream[(String, Int)] = words.map((_, 1))

    /**
      * 通过updateStateByKey来以Batch Interval为单位来对历史状态进行更新,
      * 这是功能上的一个非常大的改进,否则的话需要完成同样的目的,就可能需要把数据保存在Redis、
      * Tagyon或者HDFS或者HBase或者数据库中来不断的完成同样一个key的State更新,如果你对性能有极为苛刻的要求,
      * 且数据量特别大的话,可以考虑把数据放在分布式的Redis或者Tachyon内存文件系统中;
      * Spark2.X后mapWithState应该非常稳定了。
      */
    val wordCount: DStream[(String, Int)] = pairs.updateStateByKey((values: Seq[Int], state: Option[Int]) => {
      var newValue = state.getOrElse(0)
      for (value <- values) {
        newValue += value
      }
      Option(newValue)
    })

    /**
      * 此处的print并不会直接出发Job的执行,因为现在的一切都是在Spark Streaming框架的控制之下的,对于Spark Streaming
      * 是否触发真正的Job运行是基于设置的Duration时间间隔的
      * 需要注意的是Spark Streaming应用程序要想执行具体的Job,对Dtream就必须有output Stream操作,
      * output Stream有很多类型的函数触发,类print、saveAsTextFile、saveAsHadoopFiles等,最为重要的一个
      * 方法是foraeachRDD,因为Spark Streaming处理的结果一般都会放在Redis、DB、DashBoard等上面,foreachRDD
      * 主要就是用用来完成这些功能的,而且可以随意的自定义具体数据到底放在哪里!!!
      */
    wordCount.print()

    /**
      * Spark Streaming执行引擎也就是Driver开始运行,Driver启动的时候是位于一条新的线程中的,当然其内部有消息循环体,用于
      * 接受应用程序本身或者Executor中的消息;
      */
    // 开始提交任务
    ssc.start()
    // 线程等待,等待处理下一批次任务
    ssc.awaitTermination()
  }

    /** Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)]
      * 在调用updateStateByKey中,需要传入一个用于计算历史批次和当前批次数据的函数
      * 该函数中有几个类型:String, Seq[Int], Option[Int])]
      * String代表元组中每一个单词,也就是key
      * Seq[Int]代表当前批次相同key对应的value,比如Seq(1,1,1,1)
      * Option[Int]代表上一批次中相同key对应的累加的结果,有可能有值,有可能没有值。
      * 此时,获取历史批次的数据时,最好用getOrElse方法
      */
  val func = (it: Iterator[(String, Seq[Int], Option[Int])]) => {
    it.map(tup => {
      (tup._1, tup._2.sum + tup._3.getOrElse(0))
    })
  }
}
运行

  服务器运行nc
在这里插入图片描述
  在idea端运行编写好的程序
  服务器发送数据
在这里插入图片描述
  控制台显示结果
在这里插入图片描述

结论

  updateStateByKey它会按照时间线在每一个批次间隔返回之前的key的状态,它会对已存在的key进行state的状和每个新出现的key执行相同的更新函数操作。同时updateStateByKey,要求必须要设置checkpoint,对数据进行缓存。



第一篇:Spark Streaming状态管理函数(一)——updateStateByKey和mapWithState
第三篇:Spark Streaming状态管理函数(三)——MapWithState的使用(scala版)

猜你喜欢

转载自blog.csdn.net/m0_37914799/article/details/84703057
今日推荐