1.transformation
(1)作用:用于DStream和RDD之间交互。
package SparkReview.Streaming02
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable.ListBuffer
object LeftJoinApp {
def main(args: Array[String]): Unit = {
val sparkConf=new SparkConf().setAppName("LeftJoinApp").setMaster("local[2]")
val sc=new StreamingContext(sparkConf,Seconds(10))
//TODO...从数据一(hdfs)上去读取日志信息,数据二为黑名单,俩份数据做join只要数据一中非黑名单的数据
//数据一
val input1=new ListBuffer[(String,Long)]
input1.append(("www.ruozedata.com",9999))
input1.append(("www.baidu.com",7777))
input1.append(("www.ruozedata.com",77885))
input1.append(("www.douyu.com",9999))
input1.append(("www.baidu.com",6666))
input1.append(("www.ruozedata.com",9999))
val data1=sc.sparkContext.parallelize(input1)
//数据二
val input2=new ListBuffer[(String,Boolean)]
input2.append(("www.baidu.com",true))
val data2=sc.sparkContext.parallelize(input2)
data1.leftOuterJoin(data2).filter(x=>{
x._2._2.getOrElse(false)!=true
}).map(x=>(x._1,x._2._1)).collect().foreach(println)
//做到这一步要注意他的数据结构了,这时候在根据下面这个数据类型做个过滤
// (www.ruozedata.com,(9999,None))
//(www.baidu.com,(7777,Some(true)))
//过滤之后的数据结构变成了(www.douyu.com,(9999,None)),再做一次map
sc.start()
sc.awaitTermination()
}
}
这是RDD和RDD之间的使用,上述需求如何用DStream完成呢?
package SparkReview.Streaming02
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import scala.collection.mutable.ListBuffer
object LeftJoinStreamingApp {
def main(args: Array[String]): Unit = {
val sparkConf=new SparkConf().setMaster("local[2]").setAppName("LeftJoinStreamingApp")
val ssc=new StreamingContext(sparkConf,Seconds(10))
//数据二
val input2=new ListBuffer[(String,Boolean)]
input2.append(("wwww.baidu.com",true))
val data2=ssc.sparkContext.parallelize(input2)
//数据一
val lines=ssc.socketTextStream("192.168.137.251",8888)
lines.map(x=>(x.split(",")(0),x)).transform(rdd=>{//"wwww.baidu.com",("wwww.baidu.com",9999))
rdd.leftOuterJoin(data2) //这里需要注意JVM编译是按照顺序来的,所以data2应该放在上面
//数据结构为(wwww.baidu.com",("wwww.baidu.com",9999,Some(true)) (wwww.ruozedata.com",("wwww.ruozedata.com",8888,null))
.filter(x=>{
x._2._2.getOrElse(false)!=true
}).map(x=>(x._2._1))
}).print()
其实就是多了个transformation的操作
ssc.start()
ssc.awaitTermination()
}
}
/*
object LeftJoinStreamingApp {
def main(args: Array[String]) {
val sparkConf = new SparkConf().setMaster("local[2]").setAppName("LeftJoinStreamingApp")
val ssc = new StreamingContext(sparkConf, Seconds(30))
// 数据二: rdd
val input2 = new ListBuffer[(String,Boolean)]
input2.append(("www.baidu.com",true))
val data2 = ssc.sparkContext.parallelize(input2)
// 数据一: nc过来
val lines = ssc.socketTextStream("192.168.137.251",9888)
lines.map(x=>(x.split(",")(0), x)).transform(rdd => {
rdd.leftOuterJoin(data2).filter(x=>{
x._2._2.getOrElse(false) != true
}).map(_._2._1)
}).print()
ssc.start()
ssc.awaitTermination()
}
}
*/
2.foreachRDD和foreachPartition配合使用
(1)作用:当你处理完数据写入数据库的时候,就要用到这个俩了
import java.sql.DriverManager
import SparkReview.Streaming02.{BoneCPtest, ExampleJDBC}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
object ForeachRDDApp{
def main(args: Array[String]): Unit = {
val sparkConf=new SparkConf().setAppName("ForeachRDDApp").setMaster("local[2]")
val ssc=new StreamingContext(sparkConf,Seconds(10))
val lines=ssc.socketTextStream("192.168.137.251",8888)
lines.flatMap(_.split(",")).map(x=>(x,1)).reduceByKey(_+_).foreachRDD(rdd=>{
rdd.foreachPartition{x=>
val connection=getConnection() //executed at the driver 这有个问题,假设有一百万条数据,
// 岂不是要创建100万个链接?所以应该使用foreachpartition不用foreachrdd
x.foreach(x=>{
val word=x._1
val count=x._2
val sql=s"insert into wc(word,c) values('$word',$count)"
connection.createStatement().execute(sql)
})
connection.close()
}
})
ssc.start()
ssc.awaitTermination()
}
def getConnection()={
Class.forName("com.mysql.jdbc.Driver")
DriverManager.getConnection("jdbc:mysql://192.168.137.251:3306/g3","root","123456")
}
}
这里有个连接池的问题?记得实现!
3.Cache/persisit
(1)与rdd类似,DStreams还允许开发人员将流的数据持久化到内存中。也就是说,在DStream上使用persist()方法将自动将DStream的每个RDD持久化到内存中。如果DStream中的数据将被多次计算(例如,对同一数据的多个操作),那么这是非常有用的。对于基于窗口的操作(如reduceByWindow和reduceByKeyAndWindow)以及基于状态的操作(如updateStateByKey),这是隐式正确的。因此,由基于窗口的操作生成的DStreams将自动持久化到内存中,而无需开发人员调用persist()
长话短说就是它自己会自动persist不需要我们手工,在Dstream中不建议persisit,因为数据在不断的进来,一卡就延迟一堆。
4.Window operations
(1)使用场景:
- window length - The duration of the window (3 in the figure).
- sliding interval - The interval at which the window operation is performed (2 in the figure).
第一个时间称为窗口长度,第二个时间称为滑动长度,举例:其含义表示每隔4秒计算最近最近8秒的数据,这可以用于一些业务场景,例如网站记录,每隔1个小时计算最近两个小时的pv量,还有一种业务场景的话先在内存中做累加再更新到redis中做累加,比如说每隔5秒统计最近5秒的数据的总和,再刷到redis中做累加,因为频繁操作redis的话会存在问题.
(2)val ssc=new StreamingContext(sparkConf,Seconds(10))
此处设置的batch Interval是在spark streaming中生成基本Job的时间单位,窗口和滑动时间间隔一定是该batch Interval的整数倍,若要在内存中做简单的累加只要设置窗口长度和滑动长度相同即可。