大数据Spark实时处理--实时流处理2(Spark Streaming API)
- Spark Streaming核心概念
- 我们所谓的Spark Streaming做数据的实时处理,并不是一个真正的实时处理,是因为并非是来一条数据就处理一条数据。本质上Spark Streaming是将进来的数据流DStreams,按照我们指定的时间间隔,拆成了小批次数据,进行处理。其中每一个批次就是一个RDD。
- 官网:Spark Streaming - Spark 3.2.0 Documentation (apache.org)
- 一、StreamingContext
- 1)一定要配置master和name两个参数。
- 二、DStreams(Discretized Streams)
- 1)DStreams理解为一个持续的数据流
- 2)DStreams是Spark Streaming核心编程的抽象模型
- 3)DStreams有两个来源:源端的数据输入,是DStreams;源端数据输入后,经过一系列计算后的输出结果,依然是DStreams。代码表示
val result = lines.flatMap(_.split(",")).map((_,1))
- 三、Input DStreams and Receiver
- 1)Input DStreams就是数据源端接收过来的数据流,即从Kafka、Flume、HDFS接收过来的数据就是Input DStreams。代码表示
val lines = ssc.socketTextStream("spark000", 9527)
- 2)lines就是Input DStreams,即从netcat服务器上接收过来的数据流。
- 3)每一个Input DStreams都关联一个Receiver(除了文件流)。代码表示
val lines: ReceiverInputDStream[String]= ssc.socketTextStream("spark000", 9527)
- 4)ReceiverInputDStream是一个抽象类,是接收输入的DStream。需要启动在worker节点上的receiver,并接收外部的输入数据。然后交给spark内存,最后交给引擎做相应处理。
- 5)ReceiverInputDStream要有一个getReceiver方法.
- 6)注意1:当在本地运行Spark Streaming时,master的配置一定不能使用local或者local[1],会发生只有一个receiver接收。所以要设置local[n],n一定要大于receiver的数量。
- 7)注意2:当业务逻辑运行在集群之上,cores的数量对应于上述的local[n]的数量。cores的数量一定要大于receiver的数量。否则会发生仅接收数据,并不处理数据。
- 实战之读取文件系统的数据
- 0)Spark Streaming本地读文件系统的数据。
- 1)数据源端有2个,其中一个是basic sources。basic sources中也有2个,socket和file。socket已经ok了,其中ssc.socketTextStream()和ssc.socketStream()底层调用的都是SocketInputDStream,只是提供的API不同。
- 2)file的话,file必须兼容HDFS的API。使用的话,是通过StreamingContext.fileStream[KeyClass, ValueClass, InputFormatClass]。代码表示:
streamingContext.fileStream[KeyClass, ValueClass, InputFormatClass](dataDirectory)
- 3)file是不需要运行一个receiver来接收数据的。所以在开发过程中local可以写1。
- 4)代码部分,修改了两个地方
- 1、object NetworkWordCount 修改为 object FileWordCount
- 2、val lines = ssc.socketTextStream("spark000", 9527)修改为val lines = ssc.textFileStream("file://本地文件地址")
- 5)Spark Streaming会去监控文件夹,只要有新的数据,就会处理。可以写本地,也可以写hdfs,也可以写正则表达式。但文件格式要相同(文件后缀)。
- 6)本地启动后,再新建文本并放入。
package com.imooc.bigdata.ss import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} /* * object FileWordCount作用:完成词频统计分析 * 数据源:HDFS * * ss的编程范式 * 1)main方法 * 2)找入口点:new StreamingContext().var * 3)添加SparkConf的构造器:new SparkConf().var * 4)参数1:sparkConf放入new StreamingContext() * 5)参数2:Seconds(5)放入new StreamingContext() * 6)生成ssc:new StreamingContext(sparkConf,Seconds(5)).var * 7)对接网络数据 * ssc.socketTextStream("spark000",9527).var * 8)开始业务逻辑处理 * 启动流作业:ssc.start() * 输入数据以逗号分隔开:map是给每个单词赋值1,,然后两两相加。 lines.flatMap(_.split(",")).map((_,1)) .reduceBykey(_+_).var * 结果打印: * 终止流作业:ssc.awaitTermination() * 9)运行报错,添加val sparkConf = new SparkConf()参数 */ object FileWordCount { // *****第1步 /* 对于NetworkWordCount这种Spark Streaming编程来讲,也是通过main方法 输入main,回车 */ def main(args: Array[String]): Unit = { // *****第2步 /* 和kafka相同,找入口点 官网:https://spark.apache.org/docs/latest/streaming-programming-guide.html 要开发Spark Streaming应用程序,入口点就是:拿到一个streamingContext:new StreamingContext() 看源码:按ctrl,进入StreamingContext.scala * 关于StreamingContext.scala的描述 Main entry point for Spark Streaming functionality. It provides methods used to create DStream: [[org.apache.spark.streaming.dstream.DStream] 那 DStream是什么呢? * 目前,鼠标放在StreamingContext(),报错:不能解析构造器,所以这里缺少构造器 Cannot resolve overloaded constructor `StreamingContext` 在scala里是有构造器的,主构造器、副主构造器。 * 以下就是构造器要传的三个参数 * class StreamingContext private[streaming] ( _sc: SparkContext, _cp: Checkpoint, _batchDur: Duration ) * 这个是副主构造器1:传的是sparkContext * batchDuration是时间间隔 * def this(sparkContext: SparkContext, batchDuration: Duration) = { this(sparkContext, null, batchDuration) } * 这个是副主构造器2:传的是SparkConf * def this(conf: SparkConf, batchDuration: Duration) = { this(StreamingContext.createNewSparkContext(conf), null, batchDuration) } */ // *****第3步 /* 添加SparkConf的构造器 new SparkConf().var 然后选择sparkConf。不建议加类型 当打jar包时,两个参数要注释 */ val sparkConf = new SparkConf() .setAppName(this.getClass.getSimpleName) .setMaster("local[2]") // *****第2步 /* new StreamingContext() */ // *****第4步 /* 将第3步中新生成的sparkConf,放入new StreamingContext()括号中。 */ // *****第5步 /* * 添加时间间隔Duration(毫秒),可以看一下源码 * 使用 * object Seconds { def apply(seconds: Long): Duration = new Duration(seconds * 1000) } * 并导入org.apache的包,往Seconds()放5 * 意味着指定间隔5秒为一个批次 */ // *****第6步 /* new StreamingContext(sparkConf,Seconds(5)).var 输入ssc */ val ssc = new StreamingContext(sparkConf, Seconds(5)) // TODO... 对接业务数据 // *****第7步:先调用start启动 /* Creates an input stream from TCP source hostname:port. Data is received using a TCP socket and the receive bytes is interpreted as UTF8 encoded `\n` delimited */ //val lines = ssc.socketTextStream("spark000", 9527) val lines = ssc.textFileStream("file:///C://Users//jieqiong//Desktop//tmp//ss") // TODO... 业务逻辑处理 // *****第9步:输入数据以逗号分隔,并打印结果 val result = lines.flatMap(_.split(",")).map((_,1)) .reduceByKey(_+_) result.print() // *****第8步:先调用start启动\终止 ssc.start() ssc.awaitTermination() } }
- 常用Transformation操作
- 0)官网:Spark Streaming - Spark 3.2.0 Documentation (apache.org)
- 1)Spark Streaming中,基于DStreams的一些Transformation操作
- 2)作用于DStreams的Transformation操作,和RDD是非常类似的。Transformation中的数据是允许修改的。Transformation支持许多方法的:map、flatMap、filter、glom等。
- 3)开启dfs、yarn、master、Zookeeper、并开启端口号nc -lk 9527
- 4)在命令行中输入数据,开始测试:
20221212,test
20221224,pk
20221212,jieqiong
package com.imooc.bigdata.ss import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} object TransformApp { // *****第1步 /* 对于NetworkWordCount这种Spark Streaming编程来讲,也是通过main方法 输入main,回车 */ def main(args: Array[String]): Unit = { // *****第2步 /* 和kafka相同,找入口点 官网:https://spark.apache.org/docs/latest/streaming-programming-guide.html 要开发Spark Streaming应用程序,入口点就是:拿到一个streamingContext:new StreamingContext() 看源码:按ctrl,进入StreamingContext.scala * 关于StreamingContext.scala的描述 Main entry point for Spark Streaming functionality. It provides methods used to create DStream: [[org.apache.spark.streaming.dstream.DStream] 那 DStream是什么呢? * 目前,鼠标放在StreamingContext(),报错:不能解析构造器,所以这里缺少构造器 Cannot resolve overloaded constructor `StreamingContext` 在scala里是有构造器的,主构造器、副主构造器。 * 以下就是构造器要传的三个参数 * class StreamingContext private[streaming] ( _sc: SparkContext, _cp: Checkpoint, _batchDur: Duration ) * 这个是副主构造器1:传的是sparkContext * batchDuration是时间间隔 * def this(sparkContext: SparkContext, batchDuration: Duration) = { this(sparkContext, null, batchDuration) } * 这个是副主构造器2:传的是SparkConf * def this(conf: SparkConf, batchDuration: Duration) = { this(StreamingContext.createNewSparkContext(conf), null, batchDuration) } */ // *****第3步 /* 添加SparkConf的构造器 new SparkConf().var 然后选择sparkConf。不建议加类型 当打jar包时,两个参数要注释 */ val sparkConf = new SparkConf() .setAppName(this.getClass.getSimpleName) .setMaster("local[2]") // *****第2步 /* new StreamingContext() */ // *****第4步 /* 将第3步中新生成的sparkConf,放入new StreamingContext()括号中。 */ // *****第5步 /* * 添加时间间隔Duration(毫秒),可以看一下源码 * 使用 * object Seconds { def apply(seconds: Long): Duration = new Duration(seconds * 1000) } * 并导入org.apache的包,往Seconds()放5 * 意味着指定间隔5秒为一个批次 */ // *****第6步 /* new StreamingContext(sparkConf,Seconds(5)).var 输入ssc */ val ssc = new StreamingContext(sparkConf, Seconds(5)) //这里的编程模型是RDD val data = List("pk") val dataRDD = ssc.sparkContext.parallelize(data).map(x => (x, true)) // TODO... 对接业务数据 // *****第7步:先调用start启动 /* Creates an input stream from TCP source hostname:port. Data is received using a TCP socket and the receive bytes is interpreted as UTF8 encoded `\n` delimited */ /* 20221212,pk => (pk, 20221212,pk) 20221212,test */ val lines = ssc.socketTextStream("spark000", 9527) //这里的编程模型是DStream,DStream和RDD做join lines.map(x => (x.split(",")(1), x)) .transform(rdd => { rdd.leftOuterJoin(dataRDD) .filter(x => { x._2._2.getOrElse(false) != true }).map(x => x._2._1) }).print() // *****第8步:先调用start启动\终止 ssc.start() ssc.awaitTermination() } }
- 实战之带状态的应用程序开发
- 1)累加已传值,将目前上传值和历史上传值进行拼接:updateStateByKey(func)
- 2)将结果进行累加输出到控制台,记得开启端口
package com.imooc.bigdata.ss import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} /* * object NetworkWordCount作用:完成词频统计分析 (带state) * 数据源:是基于端口、网络即nc的方式,造数据 * * ss的编程范式 * 1)main方法 * 2)找入口点:new StreamingContext().var * 3)添加SparkConf的构造器:new SparkConf().var * 4)参数1:sparkConf放入new StreamingContext() * 5)参数2:Seconds(5)放入new StreamingContext() * 6)生成ssc:new StreamingContext(sparkConf,Seconds(5)).var * 7)对接网络数据 * ssc.socketTextStream("spark000",9527).var * 8)开始业务逻辑处理 * 启动流作业:ssc.start() * 输入数据以逗号分隔开:map是给每个单词赋值1,,然后两两相加。 lines.flatMap(_.split(",")).map((_,1)) .reduceBykey(_+_).var * 结果打印: * 终止流作业:ssc.awaitTermination() * 9)运行报错,添加val sparkConf = new SparkConf()参数 */ object StateNetworkWordCount { // *****第1步 /* 对于NetworkWordCount这种Spark Streaming编程来讲,也是通过main方法 输入main,回车 */ def main(args: Array[String]): Unit = { // *****第2步 /* 和kafka相同,找入口点 官网:https://spark.apache.org/docs/latest/streaming-programming-guide.html 要开发Spark Streaming应用程序,入口点就是:拿到一个streamingContext:new StreamingContext() 看源码:按ctrl,进入StreamingContext.scala * 关于StreamingContext.scala的描述 Main entry point for Spark Streaming functionality. It provides methods used to create DStream: [[org.apache.spark.streaming.dstream.DStream] 那 DStream是什么呢? * 目前,鼠标放在StreamingContext(),报错:不能解析构造器,所以这里缺少构造器 Cannot resolve overloaded constructor `StreamingContext` 在scala里是有构造器的,主构造器、副主构造器。 * 以下就是构造器要传的三个参数 * class StreamingContext private[streaming] ( _sc: SparkContext, _cp: Checkpoint, _batchDur: Duration ) * 这个是副主构造器1:传的是sparkContext * batchDuration是时间间隔 * def this(sparkContext: SparkContext, batchDuration: Duration) = { this(sparkContext, null, batchDuration) } * 这个是副主构造器2:传的是SparkConf * def this(conf: SparkConf, batchDuration: Duration) = { this(StreamingContext.createNewSparkContext(conf), null, batchDuration) } */ // *****第3步 /* 添加SparkConf的构造器 new SparkConf().var 然后选择sparkConf。不建议加类型 当打jar包时,两个参数要注释 */ val sparkConf = new SparkConf() .setAppName(this.getClass.getSimpleName) .setMaster("local[2]") // *****第2步 /* new StreamingContext() */ // *****第4步 /* 将第3步中新生成的sparkConf,放入new StreamingContext()括号中。 */ // *****第5步 /* * 添加时间间隔Duration(毫秒),可以看一下源码 * 使用 * object Seconds { def apply(seconds: Long): Duration = new Duration(seconds * 1000) } * 并导入org.apache的包,往Seconds()放5 * 意味着指定间隔5秒为一个批次 */ // *****第6步 /* new StreamingContext(sparkConf,Seconds(5)).var 输入ssc */ val ssc = new StreamingContext(sparkConf, Seconds(5)) //将历史数据放到checkpoint中 ssc.checkpoint("log-ss") // TODO... 对接业务数据 // *****第7步:先调用start启动 /* Creates an input stream from TCP source hostname:port. Data is received using a TCP socket and the receive bytes is interpreted as UTF8 encoded `\n` delimited */ val lines = ssc.socketTextStream("spark000", 9527) // TODO... 业务逻辑处理 // *****第9步:输入数据以逗号分隔,并打印结果 val result = lines.flatMap(_.split(",")).map((_,1)) .updateStateByKey[Int](updateFunction _) // .reduceByKey(_+_) result.print() // *****第8步:先调用start启动\终止 ssc.start() ssc.awaitTermination() } def updateFunction(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = { //使用新值,结合已有的老值,进行fun的操作 val current = newValues.sum val old = runningCount.getOrElse(0) Some(current + old) } }