Scala/Spark--实现WordCount

v1

package cn.hanjiaxiaozhi.exercise

import scala.collection.mutable

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 9:27
 * Desc 演示使用Scala函数式编程API完成WordCount-v1-入门程序员版
 */
object WordCount_1 {
  def main(args: Array[String]): Unit = {
    //1.准备数据
    val lines = List("hadoop hive spark flink flume", "spark spark hadoop hbase sqoop storm")
    //2.准备一个空的可变集合用来储存结果如:单词->数量,map的kv结构
    val map = mutable.Map[String, Int]()
    //3.将数据中的单词变成一个个的拆分出来放到一个集合中
    val words: List[String] = lines.flatMap(_.split(" "))
    //println(words)
    //List(hadoop, hive, spark, flink, flume, spark, spark, hadoop, hbase, sqoop, storm)
    //4.遍历words单词,并判断map中是否有这个单词
    for (word <- words) {
      /*if (map.contains(word)) { //map中存在该单词
        val count: Int = map(word) //取出原来的count
        map.put(word, count + 1) //将原来的count+1之后覆盖之前的值
      } else { //map中不存在该单词,直接记为(单词,1)
        map.put(word, 1)
      }*/
      //上面的逻辑就是从map中取出word对应的数量如果有则取出来+1,没有则为0+1,那么可以使用下面一行代码搞定
      map.put(word, map.getOrElse(word,0) + 1)
    }
    //循环结束,map中就存放了WordCount的结果
    //可以直接打印map
    //println(map)
    //Map(spark -> 3, hadoop -> 2, sqoop -> 1, flink -> 1, hive -> 1, storm -> 1, flume -> 1, hbase -> 1)

    //也可以遍历map
    //map.foreach((t:(String,Int))=>println(t._1,t._2))
    //map.foreach(t=>println(t._1,t._2))
    map.foreach{case (k,v) => println(k,v)} //模式匹配--先看懂输出,后面会学
    //(spark,3)
    //(hadoop,2)
    //(sqoop,1)
    //(flink,1)
    //(hive,1)
    //(storm,1)
    //(flume,1)
    //(hbase,1)
  }
}

v2

package cn.hanjiaxiaozhi.exercise

import scala.collection.mutable

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 9:27
 * Desc 演示使用Scala函数式编程API完成WordCount-v2-初级程序员版
 */
object WordCount_2 {
  def main(args: Array[String]): Unit = {
    //1.准备数据
    val lines = List("hadoop hive spark flink flume", "spark spark hadoop hbase sqoop storm")
    //2.将数据中的单词变成一个个的拆分出来放到一个集合中
    val words: List[String] = lines.flatMap(_.split(" "))
    //println(words)
    //List(hadoop, hive, spark, flink, flume, spark, spark, hadoop, hbase, sqoop, storm)
    //3.将每个单词记为1
    //words.map((word:String)=>(word,1))
    //words.map(word=>(word,1))
    //List[(spark, 1),(spark, 1),(hadoop, 1)....]
    val wordAndOnes: List[(String, Int)] = words.map((_,1))
    //4.将相同的单词分到一组去
    //Map[spark, List[(spark, 1),(spark, 1),(spark, 1)]]
    //Map[hadopp, List[(hadopp, 1),(hadopp, 1)]]
    //....
    val groupedMap: Map[String, List[(String, Int)]] = wordAndOnes.groupBy(_._1)
    println(groupedMap)
    //5.将单词对应的1全部加起来
    //groupedMap.map()//如果是map要对里面的kv都进行出来,而我们只需要对v进行处理,k不变就行了,所以可以用mapValues
    //val wordAndCount: Map[String, Int] = groupedMap.map(t=>(t._1,t._2.length))
    val wordAndCount: Map[String, Int] = groupedMap.mapValues(_.length)//mapValues表示key不变,对value进行处理
    //6.输出结果
    wordAndCount.foreach(println)
    //(hadoop,2)
    //(spark,3)
    //....
  }
}

v3

package cn.hanjiaxiaozhi.exercise
​
import java.io.{File, PrintWriter}import scala.io.Source
​
/**
 * Author hanjiaxiaozhi
 * Date 2020/7/16 9:27
 * Desc 演示使用Scala函数式编程API完成WordCount-v3-中级程序员版
 */
object WordCount_3 {
  def main(args: Array[String]): Unit = {
    //1.指定要读取的文件夹--可以直接使用JavaIO
    val dir: File = new File("D:\\scala\\data")//2.获取文件夹下所有的文件
    val files: Array[File] = dir.listFiles()
    //files.foreach(println)
    //D:\scala\data\1.txt
    //D:\scala\data\2.txt
    //D:\scala\data\3.txt//3.操作每一个文件,读取里面的内容并做WordCount,得出/返回每个文件中的(单词,数量)
    val tempResult: Array[Map[String, Int]] = files.map(file => {
      //4.读取每一个文件--可以直接用JavaIO,也可以用Scala封装好的
      val lines: Iterator[String] = Source.fromFile(file).getLines()
      //lines.foreach(println)
      //hello world hadoop hive
      //sqoop hadoop hello world
      //....
      //5.切分出每个单词
      val wrods: Iterator[String] = lines.flatMap(_.split(" "))//6.每个单词记为1
      val wrodAndOnes: Iterator[(String, Int)] = wrods.map((_, 1))//7.分组--Iterator转为List才有groupBy方法
      //Map[hello, List[(hello, 1),(hello, 1)]]
      //Map[hadoop, List[(hadoop, 1),(hadoop,1)]]
      val groupedMap: Map[String, List[(String, Int)]] = wrodAndOnes.toList.groupBy(_._1)//8.聚合统计出每个文件的(单词,数量)
      val tempWordAndCount: Map[String, Int] = groupedMap.mapValues(_.length)//9.返回每个文件中的(单词,数量)
      tempWordAndCount
    })
    //上面的执行完之后,tempResult中存放的就是每一个文件中的(单词,数量)---相当于各个文件的局部聚合结果//目标:将上面的每个文件的局部聚合结果汇总为一个最终的结果
    //Array[Map[(hello, 2)...],Map[(hello, 2)...],Map[(hello, 2)...]]
    //tempResult: Array[Map[String, Int]]
    //10.将上面的Array[Map[String, Int]]两层集合直接压扁为Array[(String, Int)]一层集合
    // Array[(hello, 2),(hello, 2),(hello, 2)...]
    val tuples: Array[(String, Int)] = tempResult.flatten
​
    //11.分组
    //Map[hello, Array[(hello, 2),(hello, 2),(hello, 2)]]
    val grouped: Map[String, Array[(String, Int)]] = tuples.groupBy(_._1)
    //=====================上面应该都能听懂=================//12.聚合--要将Array中的元组中的2全部加起来
    //val result: Map[String, Int] = grouped.mapValues(_.reduce((t1:(String,Int),t2:(String,Int))=>(t1._2 + t2._2)))
    //val result: Map[String, Int] = grouped.mapValues((arr:Array[(String,Int)])=>arr.reduce((t1:(String,Int),t2:(String,Int))=>(t1._2 + t2._2)))
    //上面的尝试一直报类型不匹配-换一种思路,用fold
    //原因就是因为:第一个t:除了第一次表示元素,以后都表示上一次的累计结果,累加结果中没有_2,所以用reduce不行//_.foldLeft中的下划线表示Array
    //_+_._2中的第一个下划线表示历史值
    //_+_._2中的第二个下划线表示下一个(单词,数量)
    //_+_._2中的._2表示取元组中的数量
   //val result: Map[String, Int] = grouped.mapValues(_.foldLeft(0)(_+_._2))
    //也可以直接使用sum
    val result: Map[String, Int] =grouped.mapValues(_.map(t=>t._2).sum)
​
​
    //13.结果输出到控制台
    result.foreach(println)
    //(hello,6)
    //...//14结果输出到文件
    val writer = new PrintWriter(new File("D:\\scala\\result.txt"))
    for((k,v) <-result ){
      writer.println(k + " : " + v)
    }
    writer.close()
  }
}

v4 Spark

使用Spark

sc.textFile("file:///root/words.txt")
.flatMap(_.split(" "))
.map((_,1))
.reduceByKey(_ + _)
.collect
package cn.hanjiaxiaozhi.hello

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/20 15:49
 * Desc 演示使用Spark编写WordCount
 */
object WordCount {
  def main(args: Array[String]): Unit = {
    //1.创建sc-Spark执行环境
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")//local[*]表示在本地以多线程的方式模式Spark集群运行,和上午在spark-shell中演示的local本地模式类似,*表示使用本地的所有资源
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")//表示将后续的日志级别设置为warn,减少不必要输出

    //2.读取文件
    //A Resilient Distributed Dataset (RDD)弹性分布式数据集,后续会详细讲解,今天暂时理解为分布式集合,但是使用起来和本地集合一样简单
    //RDD[一行行的单词]
    val fileRDD: RDD[String] = sc.textFile("file:///D:\\data\\spark\\words.txt")

    //3.数据处理-WordCount
    //3.1切分每一行单词并压扁为一个集合
    //RDD[一个一个的单词]
    val wordRDD: RDD[String] = fileRDD.flatMap(_.split(" "))
    //3.2每个单词记为1
    //RDD[(hello, 1),(hello, 1),(hello, 1)...(you,1)..]
    val wordAndOneRDD: RDD[(String, Int)] = wordRDD.map((_,1))
    //3.3分组聚合--以前得groupBy之后在累加聚合,现在可以使用reduceByKey一步搞定
    //reduceByKey后面会单独讲,今天直接用,可以直接理解为按照Key进行聚合,效果=groupBy+聚合
    val wordAndCount: RDD[(String, Int)] = wordAndOneRDD.reduceByKey(_+_)

    //4.输出结果
    //上面的RDD[(String, Int)]是分布式集合,所以先收集为本地集合再输出到控制台
    val result: Array[(String, Int)] = wordAndCount.collect()
    result.foreach(println)

    //5.关闭sc
    sc.stop()
  }
}

v5SparkSQL的DSL风格/SQL风格

package cn.hanjiaxiaozhi.sql
​
import org.apache.spark.SparkContext
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 9:22
 * Desc 使用SparkSQL完成WordCount
 */
object WordCount {
  def main(args: Array[String]): Unit = {
    //1.准备SparkSQL执行环境-SparkSession
    val spark: SparkSession = SparkSession.builder().appName("sql").master("local[*]").getOrCreate()
    val sc: SparkContext = spark.sparkContext
    sc.setLogLevel("WARN")
    import spark.implicits._
​
    //2.读取数据获取DataFrame/DataSet
    //可以使用sc.textFile("路径")进行读取,然后返回RDD,再转为DataFrame/DataSet
    //也可以直接使用SparkSession的读取方法直接返回DataFrame/DataSet
    val df: DataFrame = spark.read.text("D:\\data\\words.txt")
    val ds: Dataset[String] = spark.read.textFile("D:\\data\\words.txt")//3.处理数据
    //对每一行单词进行按照空格切分
    //df.flatMap((line:String)=>{line.split(" ")})//DataFrame没有泛型,不支持支持参数类型
    //df.flatMap(_.split(" "))//DataFrame没有泛型,不知道_表示字符串
    //上面的DF为什么不可以使用flatMap(_.split(" "))? ---因为:DataFrame没有泛型!不知道_表示字符串
    //wordDS: Dataset[一个个的单词]
    val wordDS: Dataset[String] = ds.flatMap(_.split(" "))
    wordDS.show(false)
    wordDS.printSchema()
    /*
+-----+
|value|
+-----+
|hello|
|me   |
|you  |
  ....
root
 |-- value: string (nullable = true)
     *///4.得出WordCount结果
    //TODO 1 DSL风格
    wordDS.groupBy("value")
      .count()
      .orderBy($"count".desc)
      .show(false)
​
​
    //TODO 2 SQL风格
    wordDS.createOrReplaceTempView("t_word")
    val sql:String =
      """
        |select value as word,count(*) as counts
        |from t_word
        |group by value
        |order by counts desc
        |""".stripMargin
    spark.sql(sql).show(false)}
}

v6Spark Streaming

package cn.hanjiaxiaozhi.stream

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext, streaming}
import org.apache.spark.streaming.{Seconds, StreamingContext}

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 15:37
 * Desc 使用SparkStreaming监听node01:9999发送的实时数据并做WordCount
 */
object WordCount1 {
  def main(args: Array[String]): Unit = {
    //1.准备SparkStreaming执行环境-StreamingContext
    //spark.master should be set as local[n], n > 1 in local mode
    //如果写的是local[n]在SparkStreaming中,n必须>1,因为需要一个接收,一个计算
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    //batchDuration the time interval at which streaming data will be divided into batches
    //Seconds(5)表示每隔5s对数据进行一个批次的划分,也就是微批处理的时间划分间隔
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))

    //2.监听node01:9999发送的实时数据
    val dataDStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //3.实时处理数据并做WordCount
    //对DStream进行WordCount的操作其实就是对每5s一个批次的RDD进行WordCount
    val wordsDStream: DStream[String] = dataDStream.flatMap(_.split(" "))
    val wordAndOneDStream: DStream[(String, Int)] = wordsDStream.map((_,1))
    val resultDStream: DStream[(String, Int)] = wordAndOneDStream.reduceByKey(_+_)

    //4.输出结果到控制台
    resultDStream.print()

    //5.启动实时程序-等待关闭
    ssc.start()
    ssc.awaitTermination()

  }
}
  • 可以完成简单的WordCount, 但是只能对当前批次的数据进行累计
  • 无法将下一批次的数据和之前的历史结果进行累加
  • 如前5s发了hello world hello world,那么结果为 hello 2 world 2
  • 隔了5s后又发了一次hello world 那么结果为 hello 1 world 1 而不是和之前的累加为 hello 3 world 3
  • 所以要使用有状态计算来完成和历史数据的累加

v7Spark Streaming有状态计算:和历史数据进行累加

package cn.hanjiaxiaozhi.stream

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 15:37
 * Desc 使用SparkStreaming监听node01:9999发送的实时数据并做WordCount,并将当前批次数据和历史数据进行累加/聚合
 */
object WordCount2 {
  def main(args: Array[String]): Unit = {
    //1.准备SparkStreaming执行环境-StreamingContext
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))

    //The checkpoint directory has not been set. Please set it by StreamingContext.checkpoint().
    //注意:如果要进行历史值聚合/累加,那么需要将历史值找个目录存起来,SparkStreaming会自动使用该目录
    ssc.checkpoint("./sscckp")//实际中给一个HDFS路径,本地测试给个本地的也行

    //2.监听node01:9999发送的实时数据
    val dataDStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //3.实时处理数据并做WordCount
    //对DStream进行WordCount的操作其实就是对每5s一个批次的RDD进行WordCount
    val wordsDStream: DStream[String] = dataDStream.flatMap(_.split(" "))
    val wordAndOneDStream: DStream[(String, Int)] = wordsDStream.map((_,1))
    //val resultDStream: DStream[(String, Int)] = wordAndOneDStream.reduceByKey(_+_)
    //================================有状态计算===========================================
    //我们需要对wordAndOneDStream中的每一条数据(单词,1),要和之前上一批次的历史聚合结果进行累加
    //那么某一个单词/key的数据第一次进来,历史值应该是0
    ///也就是按照单词的key和key对应的历史值聚合/累加
    //所以可以使用SparkStreaming提供的updateStateByKey

    //updateFunc: (Seq[V], Option[S]) => Option[S]
    //定义一个函数updateFunc,完成当前批次的(单词,1)和历史值进行累加/聚合
    //currentValues:Seq[Int]表示当前批次的数据,如发送了两次hello,那么就是(hello,1) (hello,1),byKey之后就是[1,1]
    //historyValue:Option[Int]表示当前key的历史值,历史值如果没有就是0
    //返回值: 当前批次的数据 + 历史值
    val updateFunc = (currentValues:Seq[Int],historyValue:Option[Int])=>{
      //getOrElse(0)表示如果有则返回历史值,如果没有则返回0
      val currentResult: Int = currentValues.sum + historyValue.getOrElse(0)
      //Some表示有值,是返回类型Option的子类型
      Some(currentResult)
    }
    val resultDStream: DStream[(String, Int)] = wordAndOneDStream.updateStateByKey(updateFunc)

    //4.输出结果到控制台
    resultDStream.print()

    //5.启动实时程序-等待关闭
    ssc.start()
    ssc.awaitTermination()

  }
}

v8Spark Streaming重启后的状态恢复

package cn.hanjiaxiaozhi.stream

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 15:37
 * Desc 使用SparkStreaming监听node01:9999发送的实时数据并做WordCount,并将当前批次数据和历史数据进行累加/聚合,然后再完成重启之后的状态恢复
 */
object WordCount3 {
  val chkpath = "./sscckp"

  def createFunction():StreamingContext={
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))
    ssc.checkpoint(chkpath)//实际中给一个HDFS路径,本地测试给个本地的也行
    //监听node01:9999发送的实时数据
    val dataDStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //实时处理数据并做WordCount
    //对DStream进行WordCount的操作其实就是对每5s一个批次的RDD进行WordCount
    val wordsDStream: DStream[String] = dataDStream.flatMap(_.split(" "))
    val wordAndOneDStream: DStream[(String, Int)] = wordsDStream.map((_,1))

    // updateFunc: (Seq[V], Option[S]) => Option[S]
    //currentValues:Seq[Int]:表示当前批次数据,如输入hello hello ,那么map之后为(hello,1)(hello,1) ,byKey之后为[1,1]
    //historyValue:Option[Int]:表示当前key对应的历史值,如果没有则为0
    //返回: currentValues值 + historyValue值
    val updateFunc = (currentValues:Seq[Int],historyValue:Option[Int])=>{
      //getOrElse(0)表示如果有历史值则返回,没有则返回0
      val currentResult: Int = currentValues.sum + historyValue.getOrElse(0)
      //Option(currentResult)
      Some(currentResult)
    }
    val resultDStream: DStream[(String, Int)] = wordAndOneDStream.updateStateByKey(updateFunc)

    //输出结果到控制台
    resultDStream.print()
    ssc
  }

  def main(args: Array[String]): Unit = {
    //1.使用StreamingContext.getOrCreate,表示如果chkpath有则根据chkpath返回,没有则创建
    val ssc: StreamingContext = StreamingContext.getOrCreate(chkpath,createFunction)

    //2.启动实时程序-等待关闭
    ssc.start()
    ssc.awaitTermination()

  }
}

v9Spark Streaming--reduceByKeyAndWindow窗口聚合

  • 我们已经可以对实时数据按照时间批次进行划分,然后计算当前批次的数据或者将当前批次的数据和历史数据/历史状态进行聚合/进行有状态计算
  • 但是如果有了新的需求就无法满足了,如
    • 每隔5S计算最近10S的数据
    • 每隔1分钟计算最近24小时的热搜词汇排行榜…
  • 而要完成这些需求就得使用SparkStreaming提供的窗口操作(和SparkCore的开窗函数不一样)

每隔5S计算最近10S的数据的WordCount

package cn.hanjiaxiaozhi.stream

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Minutes, Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 15:37
 * Desc 使用SparkStreaming监听node01:9999发送的实时数据并做WordCount,每隔5S计算最近10S的数据的WordCount
 */
object WordCount4 {
  def main(args: Array[String]): Unit = {
    //1.准备环境
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))//每隔5s对数据划分一个批次,形成一个个的RDD

    //2.监听node01:9999端口的数据
    val dataDStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)

    //3.处理数据做WordCount
    val wordsDStream: DStream[String] = dataDStream.flatMap(_.split(" "))
    val wordAndOneDStream: DStream[(String, Int)] = wordsDStream.map((_,1))
    //val resultDStream: DStream[(String, Int)] = wordAndOneDStream.reduceByKey(_+_)
    /* 使用reduceByKeyAndWindow完成每隔5S(滑动间隔)计算最近10S(窗口大小)的数据
     * @param windowDuration 窗口大小
     * @param slideDuration  滑动间隔
     * must be a multiple of this DStream's batching interval
     * 必须是微批划分时间间隔的整数倍
     */
    val resultDStream: DStream[(String, Int)] = wordAndOneDStream.reduceByKeyAndWindow((a:Int,b:Int)=>a+b,Seconds(10),Seconds(5))
    //以后公司中做业务开发根据需求设置参数大小进行统计即可
    //如:每隔1分钟(滑动间隔),计算最近1小时(窗口大小)的各个广告位的点击量(后可能要做广告位分时定价)
    //wordAndOneDStream.reduceByKeyAndWindow((a:Int,b:Int)=>a+b,Minutes(60),Minutes(1))

    //4.输出结果
    resultDStream.print()

    //5.启动并等待结束
    ssc.start()
    ssc.awaitTermination()

  }
}

每隔1分钟(滑动间隔),计算最近1小时(窗口大小)的各个广告位的点击量

新闻热搜排行榜
后可能要做广告位分时定价

package cn.hanjiaxiaozhi.stream
​
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext, rdd}/**
 * Author hanjiaxiaozhi
 * Date 2020/7/25 15:37
 * Desc 使用SparkStreaming监听node01:9999发送的实时数据并做WordCount,每隔5S计算最近10S的热搜词汇排行榜
 */
object WordCount5 {
  def main(args: Array[String]): Unit = {
    //1.准备环境
    val conf: SparkConf = new SparkConf().setAppName("wc").setMaster("local[*]")
    val sc: SparkContext = new SparkContext(conf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))//每隔5s对数据划分一个批次,形成一个个的RDD//2.监听node01:9999端口的数据
    val dataDStream: ReceiverInputDStream[String] = ssc.socketTextStream("node01",9999)//3.处理数据做WordCount
    val wordsDStream: DStream[String] = dataDStream.flatMap(_.split(" "))
    val wordAndOneDStream: DStream[(String, Int)] = wordsDStream.map((_,1))
    /* 使用reduceByKeyAndWindow完成每隔5S计算最近10S的热搜词汇排行榜
     * @param windowDuration 窗口大小
     * @param slideDuration  滑动间隔
     * must be a multiple of this DStream's batching interval
     * 必须是微批划分时间间隔的整数倍
     */
    //每隔5S计算最近10S的热搜词汇排行榜
    val wordAndCountDStream: DStream[(String, Int)] = wordAndOneDStream.reduceByKeyAndWindow((a:Int,b:Int)=>a+b,Seconds(10),Seconds(5))
    //注意上面的wordAndCountDStream只是每隔5S计算最近10S的WordCount,还没有排序
    //接下来要对上面的数据进行排序,但是DStream没有排序方法
    //而我们要对DStream中的RDD中的数据进行排序,所以将对DStream的排序转换为对里面的RDD中的数据进行排序
    //所以接下来可以调用一个transform方法,表示作用于DStream中的各个RDD,可以是任意的操作,从而返回一个新的RDD
    val sortedDStream: DStream[(String, Int)] = wordAndCountDStream.transform(rdd => {
      //排序
      val sortedRDD: RDD[(String, Int)] = rdd.sortBy(_._2, false) //_._2表示按照单词数量进行排序,false表示逆序
      //取排好序的前N个词汇
      println("====top3热搜词-start====")
      sortedRDD.take(3).foreach(println)
      println("====top3热搜词-end====")
​
      sortedRDD
    })//4.输出结果
    sortedDStream.print()//这句不能省略,因为需要Output/Action操作 // No output operations registered, so nothing to execute//5.启动并等待结束
    ssc.start()
    ssc.awaitTermination()}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值