实现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()
}
}