Spark Streaming 架构:
1,Spark Streaming为每个输入源启动对应的接收器。接收器以任务的形式运行在应用的执行器进程中,从输入源收集数据并保存为 RDD。
2,收集到输入数据后会把数据复制到另一个执行器进程来保障容错性(默 认行为)。数据保存在执行器进程的内存中,和缓存 RDD 的方式一样。
3,驱动器程序中的 StreamingContext 会周期性地运行 Spark 作业来处理这些数据,把数据与之前时间区间中的 RDD 进行整合。
4,每个接收器都以 Spark 执行器程序中一个长期运行的任务的形式运行,因此会占据分配给应用的 CPU 核心。
5,我们还需要有可用的 CPU 核心来处理数据。这意味着如果要运行多个接收器,就必须至少有和接收器数目相同的核心数,还要加上用来完成计算所需要的核心数(最少一个)。
自定义 接收器
package Day3 import java.io.{BufferedReader, InputStreamReader} import java.net.Socket import org.apache.spark.storage.StorageLevel import org.apache.spark.streaming.receiver.Receiver class Streaming_(host: String, port:Int) extends Receiver[String](StorageLevel.MEMORY_ONLY){ // 接收器启动的时候调用 override def onStart(): Unit = { // 接收器是线程来实现的 new Thread("receiver"){ override def run(): Unit = { print("---------------------") // 接受数据提交给 框架 receive() } }.start() } def receive(): Unit ={ var sockt:Socket = null var input:String = null try{ // 使用 Socket 监听端口 sockt = new Socket(host,port) // 生成输入流 val reader = new BufferedReader(new InputStreamReader(sockt.getInputStream)) // // 数据传递给 executor // store(input) while (!isStopped()&& (input=reader.readLine())!=null){ store(input) input=reader.readLine() } restart("restart") }catch { case e:java.net.ConnectException => restart("restart") case t:Throwable => restart("restart") } } // 接收器关闭调用 override def onStop(): Unit = { } }
使用无状态接收器(没有使用自定义接收器)
package Day3 import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} object Wc { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local[*]").setAppName("wc") val ssc = new StreamingContext(conf,Seconds(5)) //接受的数据方式,返回的是一行一行的数据, 接受 master:9999 端口 val lineDstream = ssc.socketTextStream("master",9999) val wordsDstream = lineDstream.flatMap(_.split(" ")) val k2vDstream = wordsDstream.map((_,1)) val results = k2vDstream.reduceByKey(_+_) results.print() // 开启 Stream 启动消息采集和处理 ssc.start() /* ------------------------------------------- Time: 1561649120000 ms ------------------------------------------- (da,1) (DA,1) (ad,2) (AD,3) */ ssc.awaitTermination() }}
使用有状态接收器,实际使用了 checkpoint 保存了上次 状态
package Day3 import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} object Wc { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local[2]").setAppName("wc") val ssc = new StreamingContext(conf,Seconds(5)) ssc.checkpoint("./check") //接受的数据方式,返回的是一行一行的数据, 接受 master:9999 端口 // val lineDstream = ssc.socketTextStream("master",9999) // 使用自定义的接收器 val lineDstream = ssc.receiverStream(new Streaming_("master",9999)) val wordsDstream = lineDstream.flatMap(_.split(" ")) val k2vDstream = wordsDstream.map((_,1)) val updateFuc = (v:Seq[Int],state:Option[Int])=>{ val preStatus = state.getOrElse(0) Some(preStatus+v.sum) } val results = k2vDstream.updateStateByKey(updateFuc) results.print() // 开启 Stream 启动消息采集和处理 ssc.start() /* ------------------------------------------- Time: 1561799425000 ms ------------------------------------------- (4,1) (ss,1) (55,1) (2,1) (,3) (s,8) (234,1) (3,3) (q,3) (12,1) ... */ ssc.awaitTermination() }} /* SparkStreaming是准实时的处理框架(微批处理),按照时间段进行收集数据 以上代码运行说明 * */
以上代码运行说明
使用 Window Operations 来获取 Streaming运行状态
package Day3 import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} object Wc { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local[2]").setAppName("wc") val ssc = new StreamingContext(conf,Seconds(5)) ssc.checkpoint("./check") //接受的数据方式,返回的是一行一行的数据, 接受 master:9999 端口 // val lineDstream = ssc.socketTextStream("master",9999) // 使用自定义的接收器 val lineDstream = ssc.receiverStream(new Streaming_("master",9999)) val wordsDstream = lineDstream.flatMap(_.split(" ")) val k2vDstream = wordsDstream.map((_,1)) // val updateFuc = (v:Seq[Int],state:Option[Int])=>{ // val preStatus = state.getOrElse(0) // Some(preStatus+v.sum) // } // val results = k2vDstream.updateStateByKey(updateFuc) // 使用窗口函数 (reduce 函数,窗口大小,步长) // 每次重新计算,重复的部分删掉 // val results = k2vDstream.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(15),Seconds(10)) // 使用窗口函数 (reduce 函数,减去多余的(前五秒的),窗口大小,步长) // 这种方式更加优化,针对重复次数来计算 val results = k2vDstream.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,(a:Int,b:Int)=>a-b,Seconds(15),Seconds(10)) results.print() // 开启 Stream 启动消息采集和处理 ssc.start() /* ------------------------------------------- Time: 1561866450000 ms ------------------------------------------- (2,8) (,1) (22,1) (1,3) */ ssc.awaitTermination() }}
以上代码运行说明