DStream作为spark 流处理的数据抽象,有三个主要的特征:
1.依赖的DStream的列表
2.DStream生成RDD的时间间隔
3.用来生成RDD的方法
本篇pom.xml文件spark streaming版本为1.6.0
目录
window()
reduceByWindow()
countByWindow()
countByValueAndWindow()
reduceByKeyAndWindow()
updatestateByKey()
生成新的DStream
def window(windowDuration: Duration): DStream[T] = window(windowDuration, this.slideDuration)
def window(windowDuration: Duration, slideDuration: Duration): DStream[T] = ssc.withScope {
new WindowedDStream(this, windowDuration, slideDuration)
}
两个重载方法,第一个只传窗口长度(滑动间隔默认为实例化ssc时的时间间隔),第二个传窗口长度与滑动间隔
val ssc=new StreamingContext(sc,Seconds(1))//时间间隔为1s
val stream=xxxx //非重点,省略
stream.print()
stream.window(Seconds(4),Seconds(4)).print()
stream.window(Seconds(4),Seconds(5)).print()
第一次print():是每秒打印一次这1秒内接收的数据
第二次print():每4秒打印前4秒接收的数据
第三次print():每5秒打印最近4秒接收的数据 ,上个5秒间隔,第一秒内的数据不会打印
生成新的DStream,作用于key-value类型
def reduceByWindow(
reduceFunc: (T, T) => T,
windowDuration: Duration,
slideDuration: Duration
): DStream[T] = ssc.withScope {
this.reduce(reduceFunc).window(windowDuration, slideDuration).reduce(reduceFunc)
}
需要传3个参数,依次为reduce()方法,窗口长度,滑动长度。
该方法的主要过程是:先将时间间隔内的数据调用reduce()算子聚合,然后调window()生成新的DStream,再将各间隔聚合完的结果聚合。
val ssc=new StreamingContext(sc,Seconds(1))//时间间隔
val stream=xxxx //类型为DStream[String]
stream.print()
stream.reduce((s1,s2)=>{
s1+":"+s2
}).print()
stream.reduceByWindow((s1,s2)=>{
s1+":"+s2
},Seconds(60),Seconds(10)).print()
第一次print():每秒打印一次接收的数据
第二次print():每秒打印一次,会将每秒接收到的数据拼接成起来
第三次print():每10秒打印一次,打印最近一分钟接收的数据,并拼接
生成新的DStream
def countByWindow(
windowDuration: Duration,
slideDuration: Duration): DStream[Long] = ssc.withScope {
//reduceByWindow()第二各方法_+_为逆函数
this.map(_ => 1L).reduceByWindow(_ + _, _ - _, windowDuration, slideDuration)
}
需要两个参数,依次为:窗口长度,滑动间隔
该方法的主要过程为:先将每个元素生成长整数1,然后调用reduceByWindow()算子,将每个元素值相加。
val ssc=new StreamingContext(sc,Seconds(1))//时间间隔为1s
val stream=xxxx
stream.print()
stream.count().print()
stream.countByWindow(Seconds(10),Seconds(2)).print()
第一次print():每秒打印一次接收的数据
第二次print():每秒打印一次接收到元素的数量
第三次print():每2秒打印一次最近10秒接收到元素的数量
生成新的DStream
def countByValueAndWindow(
windowDuration: Duration,
slideDuration: Duration,
numPartitions: Int = ssc.sc.defaultParallelism)
(implicit ord: Ordering[T] = null)
: DStream[(T, Long)] = ssc.withScope {
this.map(x => (x, 1L)).reduceByKeyAndWindow(
(x: Long, y: Long) => x + y,
(x: Long, y: Long) => x - y,
windowDuration,
slideDuration,
numPartitions,
(x: (T, Long)) => x._2 != 0L
)
}
需要三个参数,依次为:窗口长度,滑动间隔,分区数(有默认值,可不传)
该方法的主要过程为:先将每个元素生成(元素,1L),然后调用reduceByKeyAndWindow(),可以理解为按key聚合,统计每个key的次数,也就是统计每个元素的次数
val ssc=new StreamingContext(sc,Seconds(1))
val stream=xxxx
stream.print()
stream.countByValue().print()
data.countByValueAndWindow(Seconds(10),Seconds(2)).print()
第一次print():每秒打印一次接收的数据
第二次print():每秒打印一次,会统计每个元素的次数
第三次print():每2秒打印最近10秒的数据,统计每个元素次数
生成新的DStream
def reduceByKeyAndWindow(
reduceFunc: (V, V) => V,
windowDuration: Duration
): DStream[(K, V)] = ssc.withScope {
reduceByKeyAndWindow(reduceFunc, windowDuration, self.slideDuration, defaultPartitioner())
}
该方法有6个重载方法,就不一一粘了,参数为:聚合函数,窗口长度
该方法主要过程为:调reduceByKey()函数
val ssc=new StreamingContext(sc,Seconds(1))
val stream=xxxx //stream类型为[String,Long]
stream.print()
data.reduceByKey((c1,c2)=>{
c1+c2
}).print()
data.reduceByKeyAndWindow((c1,c2)=>{
c1+c2
},Seconds(10)).print()
第一次print():每秒打印一次接收的数据
第二次print():每秒打印一次,计算每个key的次数
第三次print():每秒打印一次最近10秒每个key的次数
生成新的DStream
所有的window操作都是计算长度为窗口长度的数据,非window操作都是计算设置的时间间隔内的数据,而updateBykey可以理解成在无限长的时间里,为每个key保存一个状态,这个时间长度可以通过ssc.awaitTerminationOrTimeout()来控制,一般来说长度每天或每小时。
def updateStateByKey[S: ClassTag](
updateFunc: (Seq[V], Option[S]) => Option[S]
): DStream[(K, S)] = ssc.withScope {
updateStateByKey(updateFunc, defaultPartitioner())
}
当然,该方法重载方法也6个,这里只讨论上面的,传入一个更新方法,该方法两个参数:一个为当前时间间隔内数据,类型为Seq,一个为之前的数据,可能无数据(第一次提交计算的时候),类型为Option,返回值也为Option类型
下面是两个实例,求pv和uv
//pv
val stream=xxxx//类型得转化为[当前日期,1L]
stream.updateStateByKey((curvalues:Seq[Long],prevalue:Option[Long])=>{
val sum=curvalues.sum
val pre=prevalue.getOrElse(0L)
Some(sum+pre)
})
//uv 因为uv涉及到去重,故将userid放入Set里
val stream=xxxx //类型为[当前日期,userid]
stream.updateStateByKey((curvalues:Seq[Set[String]],prevalue:Option[Set[String]])=>{
var curs=Set[String]()
if(!curvalues.isEmpty){
curs=curvalues.reduce(_++_)//将两个Set集合合并
}
Some(curs++prevalue.getOrElse(Set[String]()))
})
未完待续。。。。