WordCount案例

案例一:

import org.apache.spark.streaming._ 
val ssc = new StreamingContext(sc,Seconds(5)); 
val lines = ssc.textFileStream("file:///home/software/stream"); 
//val lines = ssc.textFileStream("hdfs://hadoop01:9000/wordcount"); 
val words = lines.flatMap(_.split(" ")); 
val wordCounts = words.map((_,1)).reduceByKey(_+_); 
wordCounts.print(); 
ssc.start();

基本概念

1. StreamingContext

StreamingContext是Spark Streaming编程的最基本环境对象,就像Spark编程中的SparkContext一样。StreamingContext提供最基本的功能入口,包括从各途径创建最基本的对象DStream(就像Spark编程中的RDD)。    

创建StreamingContext的方法很简单,生成一个SparkConf实例,设置程序名,指定运行周期(示例中是5秒),这样就可以了:

val conf = new SparkConf().setAppName("SparkStreamingWordCount") 
val sc=new SparkContext(conf) 
val ssc = new StreamingContext(sc, Seconds(5))

运行周期为5秒,表示流式计算每间隔5秒执行一次。这个时间的设置需要综合考虑程序的延时需求和集群的工作负

载,应该大于每次的运行时间。    

StreamingContext还可以从一个现存的org.apache.spark.SparkContext创建而来,并保持关联,比如上面示例中的创建方法:

val ssc = new StreamingContext(sc, Seconds(5))

   StreamingContext创建好之后,还需要下面这几步来实现一个完整的Spark流式计算:

(1)创建一个输入DStream,用于接收数据;

(2)使用作用于DStream上的TransformationOutput操作来定义流式计算(Spark程序是使用Transformation和Action操作);

(3)启动计算,使用streamingContext.start();

(4)等待计算结束(人为或错误),使用streamingContext.awaitTermination();

(5)也可以手工结束计算,使用streamingContext.stop()。    

2. DStream抽象

DStream(discretized stream)是Spark Streaming的核心抽象,类似于RDD在Spark编程中的地位。DStream表示连续的数据流,要么是从数据源接收到的输入数据流,要求是经过计算产生的新数据流。DStream的内部是一个RDD序列,每个RDD对应一个计算周期。比如,在上面的WordCount示例中,每5秒一个周期,那么每5秒的数据都分别对应一个RDD,如图所示,图中的时间点1、2、3、4代表连续的时间周期。

   

spark 程序代码加密 spark程序实例_spark 程序代码加密

   

所有应用在DStream上的操作,都会被映射为对DStream内部的RDD上的操作,比如上面的WordCount示例中对lines DStream的flatMap操作,如下图

   

spark 程序代码加密 spark程序实例_FileStream_02

   

RDD操作将由Spark核心来调度执行,但DStream屏蔽了这些细节,给开发者更简洁的编程体验。当然,我们也可以直接对DStream内部的RDD进行操作(后面会讲到)。

案例二:

经过测试,案例一代码确实可以监控指定的文件夹处理其中产生的新的文件

但数据在每个新的周期到来后,都会重新进行计算

如果需要对历史数据进行累计处理 该怎么做呢

SparkStreaming提供了checkPoint机制,首先需要设置一个检查点目录,在这个目录,存储了历史周期数据。通过在临时文件中存储中间数据 为历史数据累计处理提供了可能性

import org.apache.spark.streaming._ 
val ssc = new StreamingContext(sc,Seconds(5)); 
ssc.checkpoint("file:///home/software/chk"); 
val lines = ssc.textFileStream("file:///home/software/stream"); 
val result= lines.flatMap(_.split(" ")).map((_,1)).updateStateByKey{(seq, op:Option[Int]) => 
{ Some(seq.sum +op.getOrElse(0)) }} 
result.print(); 
ssc.start();

updateStateByKey 方法说明:

1.seq:是一个序列,存的是某个key的历史数据

2.op:是一个值,是某个key当前的值

比如: (hello,1)

①seq里是空的,Some(1)=>Some(返回的是历史值的和+当前值) 
②(hello,2),seq(1) op=2 Some(1+2)
③(hello,1) , seq(1,2) op=1 Some(3+1)

案例三:

但是这上面的例子里所有的数据不停的累计 一直累计下去

很多的时候我们要的也不是这样的效果 我们希望能够每隔一段时间重新统计下一段时间的数据,并且能够对设置的批时间进行更细粒度的控制,这样的功能可以通过滑动窗口的方式来实现。

在DStream中提供了如下的和滑动窗口相关的方法:

window(windowLength, slideInterval)

windowLength:窗口长度

slideInterval:滑动区间

spark 程序代码加密 spark程序实例_FileStream_03

countByWindow(windowLength, slideInterval) 
reduceByWindow(func, windowLength, slideInterval) 
reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 
reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 
countByValueAndWindow(windowLength, slideInterval, [numTasks])

可以通过以上机制改造案例

import org.apache.spark.streaming._ 
val ssc = new StreamingContext(sc,Seconds(1)); 
ssc.checkpoint("file:///home/software/chk");
val lines = ssc.textFileStream("file:///home/software/stream"); 
val result = lines.flatMap(_.split(" ")).map((_,1)).reduceByKeyAndWindow((x:Int,y:Int)=>x+y, Seconds(5), Seconds(5) ).print()
ssc.start();

   

spark 程序代码加密 spark程序实例_数据_04

注意:窗口长度和滑动长度必须是batch size的整数倍

此外,使用窗口机制,必须要设定检查点目录