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
    
    最后找到一个最理想的参数,然后提交到集群去运行。
    
    

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