sparkStreaming介紹及sparkStreaming整合Kafka

1、sparkStreaming概述

1.1 什麼是sparkStreaming
  • Spark Streaming makes it easy to build scalable fault-tolerant streaming applications.
    • sparkStreaming是一個可以非常容易的構建可擴展、具有容錯機制的流式應用程序
    • 它就是一個實時處理的程序,數據源源不斷的來,然後它就進行實時不斷的處理。
1.2 sparkStreaming特性
  • 1、易用性

    • 可以像開發離線批處理一樣去編寫實時處理的代碼程序

    • 可以使用多種不同的語言進行代碼開發

      • java

      • scala

      • python

  • 2、容錯性

    • sparkStreaming可以實現恰好一次語義

      • 數據被處理且只被處理一次
        • 避免了數據丟失和數據的重複處理
    • 可以實現在沒有額外代碼的情況下來恢復一些丟失的工作和狀態

  • 3、可以融合到spark生態系統

    • sparkStreaming流式處理可以與批處理和交互式查詢進行結合使用

2、sparkStreaming原理

2.1 sparkStreaming計算原理
Spark Streaming 是基於spark的流式批處理引擎,其基本原理是把輸入數據以某一時間間隔批量的處理,當批處理間隔縮短到秒級時,便可以用於處理實時數據流。
2.2 sparkStreaming計算流程
sparkStreaming是某一個時間間隔的批處理,在時間維度上就劃分成了很多job,每一個job都有大量的Dstream,
對Dstream做大量的transformation轉換操作,其本質是作用在它內部的rdd上。
也就是說Dstream內部是封裝了rdd,rdd內部又有很多個分區,分區裏面纔是真正的數據。
2.3 sparkStreaming容錯性
(1)Dstream中內部是封裝了rdd,rdd自身是具有容錯機制,就是通過lineage血統來實現某些rdd的分區數據丟失之後,然後進行重新計算恢復得到。
(2)同時對於網絡數據的處理,sparkStreaming在接受到數據之後它會把網絡中的數據保留多份到其他機器,保證數據源端的安全性。


sc.textFile("/words.txt").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).collect
                rdd1  ----------------->rdd2--------->rdd3------------->rdd4
某個rdd的數據丟失之後恢復邏輯:血統+數據源
2.4 sparkStreaming實時性
storm是來一條數據就處理一條,實時性是比較高
sparkStreaming是以某一時間間隔的批量處理,它的實時性就比較低,延遲就比較高

後期再實際公司中具體使用哪一種框架,需要結合自身的一些業務場景
比如說公司的領導允許數據出現一定的延遲,對數據的實時性要求不是特別高,這個時候可以優先考慮sparkStreaming;
如果對數據的實時性非常高,就考慮storm。

3、DStream介紹

3.1 什麼是DStream
Discretized Stream是Spark Streaming的基礎抽象,代表持續性的數據流和經過各種Spark算子操作後的結果數據流。在內部實現上,DStream是一系列連續的RDD來表示。每個RDD含有一段時間間隔內的數據.
3.2 DStream上的操作分類
  • transformation(轉換)
    • 可以把一個DStream轉換生成一個新的Dstream,它也是延遲加載,不會立即觸發任務的運行
      • 類似於rdd中transformation操作
  • outputOperation (輸出)
    • 它會觸發任務的真正運行
      • 類似於rdd中action操作

4、DStream操作實戰

  • 添加依賴

            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-streaming_2.11</artifactId>
                <version>2.1.3</version>
            </dependency>
    
4.1 利用sparkStreaming接受socket數據實現單詞統計
  • 1、代碼開發
package cn.itcast.socket

import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

//todo:利用sparkStreaming接受socket數據實現單詞統計
object SparkStreamingSocket {
  def main(args: Array[String]): Unit = {
    //1、創建SparkConf
      val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocket").setMaster("local[2]")

    //2、創建SparkContext
      val sc = new SparkContext(sparkConf)
      sc.setLogLevel("warn")

   //3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔  表示每隔5s處理上一個5s的數據
      val ssc = new StreamingContext(sc,Seconds(5))

    //4、接受socket數據
      val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)

    //5、切分每一行數據
    val words: DStream[String] = socketTextStream.flatMap(_.split(" "))

    //6、每個單詞計爲1
      val wordAndOne: DStream[(String, Int)] = words.map((_,1))

    //7、相同單詞出現的1累加
      val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)

    //8、打印輸出
      result.print()


    //9、開啓流式計算
      ssc.start()
      ssc.awaitTermination()
  }
}

4.2 利用sparkStreaming接受socket數據實現所有批次單詞統計的結果累加
  • 1、代碼開發
    • updateStateByKey
      • 可以按照key去更新狀態,key—>單詞 狀態—>次數
package cn.itcast.socket

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

//todo:利用sparkStreaming接受socket數據實現所有批次單詞統計的結果累加
object SparkStreamingSocketTotal {

   //currentValues:當前批次相同的單詞出現的所有的1 (hadoop,1)(hadoop,1)(hadoop,1)----->List(1,1,1)
  //historyValues: 在之前所有批次中相同單詞出現的總次數 Option類型可以表示有值(Some)或者是沒有值(None)
  def updateFunc(currentValues:Seq[Int],historyValues:Option[Int]):Option[Int] = {
                 val newValue: Int = currentValues.sum + historyValues.getOrElse(0)
                Some(newValue)
  }

  def main(args: Array[String]): Unit = {
    //1、創建SparkConf
    val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketTotal").setMaster("local[2]")

    //2、創建SparkContext
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("warn")

    //3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔  表示每隔5s處理上一個5s的數據
    val ssc = new StreamingContext(sc,Seconds(5))

       //設置ckeckpoint目錄,用於保存每一個單詞在之前的所有批次中出現的總次數
      ssc.checkpoint("./socket")

    //4、接受socket數據
    val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)

    //5、切分每一行數據
    val words: DStream[String] = socketTextStream.flatMap(_.split(" "))

    //6、每個單詞計爲1
    val wordAndOne: DStream[(String, Int)] = words.map((_,1))

    //7、相同單詞出現的1累加
     val result: DStream[(String, Int)] = wordAndOne.updateStateByKey(updateFunc)

    //8、打印輸出
    result.print()


    //9、開啓流式計算
    ssc.start()
    ssc.awaitTermination()
  }
}

4.3 利用sparkStreaming接受socket數據實現單詞統計–使用開窗函數

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gba23OsM-1572241319579)(reduceByKeyAndWindow使用介紹.png)]

  • 1、代碼開發
    • reduceByKeyAndWindow
package cn.itcast.socket

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

//todo:利用sparkStreaming接受socket數據實現單詞統計---使用開窗函數
object SparkStreamingSocketWindow {
  def main(args: Array[String]): Unit = {
    //1、創建SparkConf
    val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketWindow").setMaster("local[2]")

    //2、創建SparkContext
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("warn")

    //3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔  表示每隔5s處理上一個5s的數據
    val ssc = new StreamingContext(sc,Seconds(5))

    //4、接受socket數據
    val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)

    //5、切分每一行數據
    val words: DStream[String] = socketTextStream.flatMap(_.split(" "))

    //6、每個單詞計爲1
    val wordAndOne: DStream[(String, Int)] = words.map((_,1))

    //7、相同單詞出現的1累加
    //reduceFunc: (V, V) => V,   就是一個函數
    //windowDuration: Duration,  窗口的長度
    //slideDuration: Duration   滑動窗口的時間間隔,它表示每隔多久計算一次
     val result: DStream[(String, Int)] = wordAndOne.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(10),Seconds(10))

    //8、打印輸出
    result.print()


    //9、開啓流式計算
    ssc.start()
    ssc.awaitTermination()


  }

}

4.4 利用sparkStreaming接受socket數據實現一定時間內的熱門詞彙
  • 1、代碼開發

  • transform

    • 實現把一個Dstream轉換生成一個新的Dstream,內部需要一個函數,函數的輸入參數就是前面Dstream中的rdd,函數返回值是一個新的RDD
  • foreachRDD

    • 它是一個outputOperation,會觸發任務的運行,需要一個函數,函數的輸入參數還是前面Dstream中的rdd,最後函數的返回值是Unit表示沒有
package cn.itcast.socket

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}

//todo:利用sparkStreaming接受socket數據實現一定時間內熱門詞彙統計
object SparkStreamingSocketWindowHotWord {
  def main(args: Array[String]): Unit = {
    //1、創建SparkConf
    val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingSocketWindowHotWord").setMaster("local[2]")

    //2、創建SparkContext
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("warn")

    //3、創建StreamingContext 需要一個SparkContext對象,還有一個批處理時間間隔  表示每隔5s處理上一個5s的數據
    val ssc = new StreamingContext(sc,Seconds(5))

    //4、接受socket數據
    val socketTextStream: ReceiverInputDStream[String] = ssc.socketTextStream("node1",9999)

    //5、切分每一行數據
    val words: DStream[String] = socketTextStream.flatMap(_.split(" "))

    //6、每個單詞計爲1
    val wordAndOne: DStream[(String, Int)] = words.map((_,1))

    //7、相同單詞出現的1累加
    //reduceFunc: (V, V) => V,   就是一個函數
    //windowDuration: Duration,  窗口的長度
    //slideDuration: Duration   滑動窗口的時間間隔,它表示每隔多久計算一次
    val result: DStream[(String, Int)] = wordAndOne.reduceByKeyAndWindow((x:Int,y:Int)=>x+y,Seconds(10),Seconds(10))

    //8、按照單詞出現的次數降序
    val finalResult: DStream[(String, Int)] = result.transform(rdd => {
      //可以使用rdd中排序的方法去操作
      val sortRDD: RDD[(String, Int)] = rdd.sortBy(_._2, false)
      //取出出現次數最多的前3位
      val top3: Array[(String, Int)] = sortRDD.take(3)
      //打印
      println("===============top3 start===============")
      top3.foreach(println)
      println("===============top3 end=================")

      sortRDD
    })

    //8、打印輸出
    finalResult.print()


    //9、開啓流式計算
    ssc.start()
    ssc.awaitTermination()

  }
}

5、sparkStreaming整合flume

5.1 poll拉模式(優先考慮)
  • 1、需要把spark-streaming-flume-sink_2.11-2.1.3.jar拷貝到flume/lib

  • 2、還需要把flume中自帶的scala依賴由2.10改成2.11

  • 3、flume配置文件

    • flume-poll-spark.conf
    #source
    a1.sources.r1.channels = c1
    a1.sources.r1.type = spooldir
    a1.sources.r1.spoolDir = /root/data
    a1.sources.r1.fileHeader = true
    
    
    #channel
    a1.channels.c1.type =memory
    a1.channels.c1.capacity = 20000
    a1.channels.c1.transactionCapacity=5000
    
    
    #sinks
    a1.sinks.k1.channel = c1
    a1.sinks.k1.type = org.apache.spark.streaming.flume.sink.SparkSink
    a1.sinks.k1.hostname=node1
    a1.sinks.k1.port = 8888
    a1.sinks.k1.batchSize= 2000
    
  • 4、可以啓動flume

    bin/flume-ng agent -n a1 -c conf -f conf/flume-poll-spark.conf -Dflume.root.logger=info,console
    
  • 5、sparkStreaming代碼開發

    • 引入依賴

              <dependency>
                  <groupId>org.apache.spark</groupId>
                  <artifactId>spark-streaming-flume_2.11</artifactId>
                  <version>2.1.3</version>
              </dependency>
      
    • 代碼開發

      package cn.itcast.flume
      
      import java.net.InetSocketAddress
      
      import org.apache.spark.storage.StorageLevel
      import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
      import org.apache.spark.streaming.{Seconds, StreamingContext}
      import org.apache.spark.{SparkConf, SparkContext}
      import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent}
      
      //todo:sparkStreaming整合flume--------poll拉模式
      object SparkStreamingPollFlume {
        def main(args: Array[String]): Unit = {
          //1、創建SparkConf
            val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingPollFlume").setMaster("local[2]")
      
          //2、創建SparkContext
            val sc = new SparkContext(sparkConf)
            sc.setLogLevel("warn")
      
          //3、創建StreamingContext
            val ssc = new StreamingContext(sc,Seconds(5))
      
          //4、通過poll拉模式獲取flume數據
         val pollingStream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,"node1",8888)
         //拉取多個flume收集到的數據
      //    val address=List(new InetSocketAddress("node1",8888),new InetSocketAddress("node2",8888),new InetSocketAddress("node3",8888))
      //    val totalFlume: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createPollingStream(ssc,address,StorageLevel.MEMORY_AND_DISK_SER_2)
      
          //flume中數據傳輸的最小單元:event    event中有什麼?  headers   body
          //  event:{"headers":xxxxx,"body":xxxxxxx}
      
          //5、獲取flume中的真實數據    new String(Array[byte])
          val data: DStream[String] = pollingStream.map(x=>new String(x.event.getBody.array()))
      
          //6、切分每一行獲取所有的單詞
            val words: DStream[String] = data.flatMap(_.split(" "))
      
          //7、每個單詞計爲1
            val wordAndOne: DStream[(String, Int)] = words.map((_,1))
      
          //8、相同單詞出現的1累加
            val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)
      
          //9、打印
            result.print()
      
          //10 開啓流式計算
            ssc.start()
            ssc.awaitTermination()
        }
      
      }
      
      
5.2 push推模式
  • 1、修改flume配置

    • vim flume-push-spark.conf
    #push mode
    a1.sources = r1
    a1.sinks = k1
    a1.channels = c1
    #source
    a1.sources.r1.channels = c1
    a1.sources.r1.type = spooldir
    a1.sources.r1.spoolDir = /root/data
    a1.sources.r1.fileHeader = true
    #channel
    a1.channels.c1.type =memory
    a1.channels.c1.capacity = 20000
    a1.channels.c1.transactionCapacity=5000
    #sinks
    a1.sinks.k1.channel = c1
    a1.sinks.k1.type = avro
    a1.sinks.k1.hostname=192.168.160.34
    a1.sinks.k1.port = 8888
    a1.sinks.k1.batchSize= 2000
    
  • 2、代碼開發

    package cn.itcast.flume
    
    import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
    import org.apache.spark.{SparkConf, SparkContext}
    import org.apache.spark.streaming.{Seconds, StreamingContext}
    import org.apache.spark.streaming.flume.{FlumeUtils, SparkFlumeEvent}
    
    //todo:sparkStreaming整合flume--------push推模式
    object SparkStreamingPushFlume {
      def main(args: Array[String]): Unit = {
        //1、創建SparkConf
        val sparkConf: SparkConf = new SparkConf().setAppName("SparkStreamingPushFlume").setMaster("local[2]")
    
        //2、創建SparkContext
        val sc = new SparkContext(sparkConf)
        sc.setLogLevel("warn")
    
        //3、創建StreamingContext
        val ssc = new StreamingContext(sc,Seconds(5))
    
        //4、通過push推模式 接受flume的數據,這裏的hostname和port端口是sparkStreaming應用程序所在服務器地址和端口
        val stream: ReceiverInputDStream[SparkFlumeEvent] = FlumeUtils.createStream(ssc,"192.168.160.34",8888)
    
    
        //5、獲取flume中的真實數據    new String(Array[byte])
        val data: DStream[String] = stream.map(x=>new String(x.event.getBody.array()))
    
        //6、切分每一行獲取所有的單詞
        val words: DStream[String] = data.flatMap(_.split(" "))
    
        //7、每個單詞計爲1
        val wordAndOne: DStream[(String, Int)] = words.map((_,1))
    
        //8、相同單詞出現的1累加
        val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)
    
        //9、打印
        result.print()
    
        //10 開啓流式計算
        ssc.start()
        ssc.awaitTermination()
      }
    
    }
    
    

6、sparkStreaming整合kafka

6.1、KafkaUtils.creataStream

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-i9kmH0VZ-1572241319581)(sparkStreaming整合kafka–基於receiver接收器.png)]

它是基於receiver接收器去拉取數據,默認數據會丟失,開啓WAL日誌把接受到的數據同步寫入到hdfs上保證數據源的安全性,後期某些rdd的部分分區數據丟失之後,可以通過血統+原始數據重新計算恢復得到。
可以保證數據不丟失,但是它保證不了數據被處理只被處理一次,會出現數據的重複處理。
  • 添加依賴

            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
                <version>2.1.3</version>
            </dependency>
    
    
  • 代碼開發

    package cn.itcast.kafka
    
    import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
    import org.apache.spark.{SparkConf, SparkContext}
    import org.apache.spark.streaming.{Seconds, StreamingContext}
    import org.apache.spark.streaming.kafka.KafkaUtils
    
    import scala.collection.immutable
    
    //todo:sparkStreaming整合kafka-----------kafka高級api(消息的偏移量保存zk,基於receiver接受器接受數據)
    object SparkStreamingKafkaReceiver {
      def main(args: Array[String]): Unit = {
        //1、創建SparkConf
        val sparkConf: SparkConf = new SparkConf()
                                      .setAppName("SparkStreamingKafkaReceiver")
                                      .setMaster("local[4]")
                //開啓WAL日誌,把接受到的數據寫入到HDFS上,保證數據源端的安全性
                                      .set("spark.streaming.receiver.writeAheadLog.enable","true")
    
        //2、創建SparkContext
        val sc = new SparkContext(sparkConf)
        sc.setLogLevel("warn")
    
        //3、創建StreamingContext
        val ssc = new StreamingContext(sc,Seconds(5))
    
          //設置一個checkpoint目錄,用於存儲接受到的數據,保證數據的安全
           ssc.checkpoint("./spark-recever")
    
        //4、指定zk地址
        val zkQuorum="node1:2181,node2:2181,node3:2181"
    
        //5、消費者組id
         val groupId="spark-receiver"
    
        //6、指定topic信息  key:表示topic的名稱,value:表示一個recevier接收器需要使用1個線程去拉取數據
        val topics=Map("heima" ->1)
    
        /**
          ssc: StreamingContext,
          zkQuorum: String,
          groupId: String,
          topics: Map[String, Int],
          storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
          */
    
        //7、通過receiver接收器接受kafka的topic數據
        //(String, String)------> 第一個String表示消息的key, 第二個String就是消息內容
         //默認使用了一個receiver接收器去接受數據,數據接受效率比較慢,後期可以使用多個receiver接收器接受數據
        //開啓3個receiver接受數據,提升數據接收的效率
        val totalReceiverSeq: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
          val kafkaStream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
          kafkaStream
        })
    
        //通過調用union方法把所有recevier接收器得到的每一個Dstream進行合併生成一個Dstream
        val totalDstream: DStream[(String, String)] = ssc.union(totalReceiverSeq)
    
       //8、獲取topic的數據
          val data: DStream[String] = totalDstream.map(x=>x._2)
    
        //9、切分每一行獲取所有的單詞
        val words: DStream[String] = data.flatMap(_.split(" "))
    
        //10、每個單詞計爲1
        val wordAndOne: DStream[(String, Int)] = words.map((_,1))
    
        //11、相同單詞出現的1累加
        val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)
    
        //12、打印
        result.print()
    
        //13 開啓流式計算
        ssc.start()
        ssc.awaitTermination()
    
      }
    }
    
    
6.2 KafkaUtils.createDirectStream(優先考慮)
  • 可以實現數據被處理且只被處理一次

    就是通過數據處理和更新偏移量這2個操作在同一事務。
    偏移量的保存可以保存在任何外部存儲介質中,比如 文件、mysql、zk、redis
    

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6slDbTq5-1572241319583)(1.png)]

  • 1、代碼開發

    package cn.itcast.kafka
    
    import kafka.serializer.StringDecoder
    import org.apache.spark.streaming.dstream.{DStream, InputDStream}
    import org.apache.spark.{SparkConf, SparkContext}
    import org.apache.spark.streaming.{Seconds, StreamingContext}
    import org.apache.spark.streaming.kafka.KafkaUtils
    
    //todo:sparkStreaming整合kafka-------使用低級api(消息的偏移量不在由zk保存,客戶端自己去維護)
    object SparkStreamingDirectKafka {
      def main(args: Array[String]): Unit = {
        //1、創建SparkConf
        val sparkConf: SparkConf = new SparkConf()
                      .setAppName("SparkStreamingDirectKafka")
                      .setMaster("local[4]")
    
        //2、創建SparkContext
        val sc = new SparkContext(sparkConf)
        sc.setLogLevel("warn")
    
        //3、創建StreamingContext
        val ssc = new StreamingContext(sc,Seconds(5))
    
          //設置checkpoint目錄------> 保存消息消費的偏移量的
            ssc.checkpoint("./spark-direct")
    
        //4、指定kafka集羣參數
          val kafkaParams=Map("bootstrap.servers"->"node1:9092,node2:9092,node3:9092","group.id" ->"spark-direct")
    
        //5、指定消費的topic名稱
          val topics=Set("heima")
         val dstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)
    
       //6、獲取topic的內容
          val data: DStream[String] = dstream.map(x=>x._2)
    
        //7、切分每一行獲取所有的單詞
        val words: DStream[String] = data.flatMap(_.split(" "))
    
        //8、每個單詞計爲1
        val wordAndOne: DStream[(String, Int)] = words.map((_,1))
    
        //9、相同單詞出現的1累加
        val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_+_)
    
        //10、打印
        result.print()
    
        //11 開啓流式計算
        ssc.start()
        ssc.awaitTermination()
    
      }
    }
    
    
  • 2、程序邏輯梳理

    這個時候並沒有使用receiver接收器去接受數據,也沒有把接受到的數據寫入到HDFS上,相對於第一套api代碼開發起來比較簡潔。
    
    要想保證數據不丟失和數據被處理且只被處理一次:
    
    (1)數據處理
    (2)保存偏移量
    
    如果可以確保這2個操作在同一事務中。
    
    
  • 3、打成jar包提交到集羣中運行

    
    在實際開發中,先進行本地測試,本地測試只是看一下程序的處理邏輯對不對,如果有問題就及時修改,如果沒有問題就把該成打成jar包提交到集羣去運行。
    
    spark-submit --master spark://node1:7077 --class cn.itcast.kafka.SparkStreamingDirectKafka --executor-memory 1g --total-executor-cores 8 original-spark_class15-1.0-SNAPSHOT.jar
    
    --executor-memory 1g --total-executor-cores 8
    這個時候就需要給當前這個任務準備資源,這個資源到底給多少合理?
    資源給多了就浪費,給少了跑的慢..
    
    我也不知道到底給定多少資源是合理的,但是我們可以找到一個比較合理的狀態。
    
    比如說我們在代碼中的設置邏輯:每隔5s就處理上一個5s的數據。
    
    由於不知道數據量,可能會出現一個批次的數據的處理時間是1分鐘。
    第一個5s的批次數據:           -------------------------------------> 1分鐘
    第二個5s的批次數據:       -------------------------------------> 1分鐘
    第三個5s的批次數據:    -------------------------------------> 1分鐘
    第四個5s的批次數據: -------------------------------------> 1分鐘
    .......
    
    上面這種情況下就會出現大量的job在等待,也就出現了數據的積壓,延遲是非常高。
    
    最理想的狀態:就是在5s之內就把5s的數據處理完成。
    這個時候可以訪問spark的web管理界面: master主機名;8080 去觀察每一個批次的數據處理時間
    
    在給定資源的時候,可以進行幾組資源參數進行測試:
    (1)--executor-memory 5g --total-executor-cores 10
    (2)--executor-memory 5g --total-executor-cores 20
    (3)--executor-memory 6g --total-executor-cores 20
    (4)--executor-memory 6g --total-executor-cores 30
    
    最後找到一個最理想的參數,然後提交到集羣去運行。
    
    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章