一、SparkStreamingWC
这里的wordcount没有实现累加,
本地运行以下代码后,在mini1上输入:nc -lk 9999
然后输入单词,在idea的控制台上就可以出现单词统计结果
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Durations, Seconds, StreamingContext}
/**
* 获取NetCat的数据并统计WordCount
*/
object StreamingWC {
def main(args: Array[String]): Unit = {
// 初始化环境
val conf = new SparkConf().setAppName("StreamingWC").setMaster("local[2]")
val sc = new SparkContext(conf)
val ssc: StreamingContext = new StreamingContext(sc, Durations.seconds(5))
// 获取NetCat的数据
val msg: ReceiverInputDStream[String] = ssc.socketTextStream("node03", 9999)
// 统计
val sumed: DStream[(String, Int)] = msg.flatMap(_.split(" ")).map((_, 1)).reduceByKey(_+_)
// 打印
sumed.print()
// 提交任务到集群
ssc.start()
// 线程等待,等待处理任务
ssc.awaitTermination()
}
}
ps:
1.在初始化ssc时,也可以不创建sc,直接val ssc: StreamingContext = new StreamingContext(conf, Durations.seconds(5))
2.不可以写成:setMaster("local"),这样只有一个线程,被接受消息的Reciver占用,负责计算的executor没有线程,所以就没有结果输出
3. 不可以写成println(sumed),
会报错:20/02/26 16:04:43 ERROR StreamingContext: Error starting the context, marking it as stopped
java.lang.IllegalArgumentException: requirement failed: No output operations registered, so nothing to execute
二、使用UpdateStateByKey实现带累加的wordcount
使用UpdateStateByKey,要指定checkpoint的位置,用来存放之前数据,然后使用UpdateStateByKey后,每次都取拉取这里的历史数据。
有个问题要注意,在hdfs上会产生很多小文件,这样很不好,所以可以写定时脚本,来删除数据,比如定时删除3天前的数据。或者选择放到mysql/redis中
import org.apache.spark.{HashPartitioner, SparkConf}
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.{Durations, StreamingContext}
/**
* 将历史批次数据应用到当前批次进行聚合
*/
object UpdateStateByKeyDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("updatestatebykey").setMaster("local[2]")
val ssc = new StreamingContext(conf, Durations.milliseconds(2000))
ssc.checkpoint("d://cp-20190716-1") //checkpoint 的位置
// 获取数据
val msg = ssc.socketTextStream("node03", 9999)
val tup: DStream[(String, Int)] = msg.flatMap(_.split(" ")).map((_, 1))
// updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)], 用于将历史数据和当前批次数据进行计算的函数
// partitioner: Partitioner, 指定分区器
// rememberPartitioner: Boolean 是否记录分区信息
val sumed: DStream[(String, Int)] = tup.updateStateByKey(
func, new HashPartitioner(ssc.sparkContext.defaultParallelism), true)
sumed.print()
ssc.start()
ssc.awaitTermination()
}
// (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)]
// 其中:K代表数据中的key
// Seq[V]代表当前批次单词出现的次数: Seq(1,1,1,1,.....)
// Option[S]代表历史批次累加的结果,可能有值(Some),也可能没有值(None),获取数据的时候可以用getOrElse
val func = (it: Iterator[(String, Seq[Int], Option[Int])]) => {
// x代表it中的其中一个元素,元素就是一个元组
it.map(x => {
(x._1, x._2.sum + x._3.getOrElse(0))
})
}
}
三、Transform
Transform原语允许DStream上执行任意的RDD-to-RDD函数。通过该函数可以方便的扩展Spark API。此外,MLlib(机器学习)以及Graphx也是通过本函数来进行结合的。
简单来说,SparkStreaming算子有限,使用Transform 就能在SparkStreaming中使用RDD的算子,
比如:
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Durations, Seconds, StreamingContext}
/**
* 获取NetCat的数据并统计WordCount
*/
object TransformWC {
def main(args: Array[String]): Unit = {
// 初始化环境
val conf = new SparkConf().setAppName("StreamingWC").setMaster("local[2]")
val sc = new SparkContext(conf)
val ssc: StreamingContext = new StreamingContext(sc, Durations.seconds(5))
// 获取NetCat的数据
val msg: ReceiverInputDStream[String] = ssc.socketTextStream("node03", 9999)
// 统计
val sumed: DStream[(String, Int)] = r1.transform(x=>{
x.flatMap(_.split(" ")).map(x=>(x,1)).reduceByKey(_+_).sortBy(_._2,false)
})
// 打印
sumed.print()
// 提交任务到集群
ssc.start()
// 线程等待,等待处理任务
ssc.awaitTermination()
}
}
四、Window Operations
Window Operations有点类似于Storm中的State,可以设置窗口的大小和滑动窗口的间隔来动态的获取当前Steaming的允许状态
示范:
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.{Durations, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}
/**
* 用窗口操作的方式进行单词计数
* 窗口大小10秒,滑动间隔10秒
*/
object WindowOperationsDemo {
def main(args: Array[String]): Unit = {
// 初始化环境
val conf = new SparkConf().setAppName("WindowOperationsDemo").setMaster("local[2]")
val sc = new SparkContext(conf)
val ssc: StreamingContext = new StreamingContext(sc, Durations.seconds(5))
// 获取NetCat的数据
val msg: ReceiverInputDStream[String] = ssc.socketTextStream("node03", 6666)
// 统计
val tup: DStream[(String, Int)] = msg.flatMap(_.split(" ")).map((_, 1))
val sumed: DStream[(String, Int)] =
tup.reduceByKeyAndWindow((x: Int, y: Int) => (x + y), Durations.seconds(10), Durations.seconds(10))
// 打印
sumed.print()
// 提交任务到集群
ssc.start()
// 线程等待,等待处理任务
ssc.awaitTermination()
}
}