spark-streaming笔记

SparkStreaming笔记
=================================================================
框架的类型:
	1.离线批处理:mapreduce、hive、SparkCore、Sparksql =》  mapreduce   spark
	2.SQL的交互式查询:hive、SparkSQL
	3.流式框架:flume、kafka、SparkStreaming
	4.实时计算:SparkStreaming
	
Strom(Clojure编写的)/jStrom(java编写的)
	完全实时的流式数据处理平台
	来一条数据就立马计算一条数据,在高并发的情况下,对机器的性能要求很高
	如果配置低了,那么就是出现很高的延迟
	
维护strom的集群的成本要比维护同等级别的Spark集群的成本要高出很多

SparkStreaming:(以微批来模拟流式)
	准实时的流式数据处理平台
	sparkstreaming是按照一个一个批次数据来进行计算的,只有当上一个批次完全计算完毕,才会计算下一个批次。否则,就会处于阻塞的状态
	问题:
		批次是如何产生的呢???
		每个批次产生的时间是由用户指定的,每到指定的时间,就会产生一个批次,这段时间内接收到的数据,就是这个批次中需要计算的数据
		
SparkStreaming程序数据处理流程:
	1.读取数据
		读取数据形成DStream
	2.数据处理
		调用DStream的API或者是将DStream转成RDD/DataFrame
	3.数据输出
		文件系统
		RDBMS(关系型数据库)
		hive、hbase。。。
		Kafka
		
=================================================================
程序的入口:
	SparkCore:SparkContext
	SparkSQL: SparkSession (基于SparkContext)
	SparkStreaming:StreamingContext(基于SparkContext)
	
核心抽象:
	SparkCore:RDD (弹性分布式数据集)
	SparkSQL: DataFrame (以RDD为基础的分布式数据集二维表格)
	SparkStreaming:DStream(Discretized Streams)(离散化的流)
=================================================================
小案例:
-1.在maven项目的pom文件中添加SparkStreaming的依赖
	<dependency>
		<groupId>org.apache.spark</groupId>
		<artifactId>spark-streaming_2.11</artifactId>
		<version>2.2.1</version>
	</dependency>
-2.使用SparkStreaming程序读取socket的传输的数据,然后计算词频统计,最后在控制台打印
	-2.1安装netcat
		方式1:离线安装
			-1.将nc-1.84-22.el6.x86_64.rpm上传到linux
			-2.使用root用户安装nc-1.84-22.el6.x86_64.rpm
				命令:rpm -ivh nc-1.84-22.el6.x86_64.rpm
				安装成功之后,退出root,exit
		方式2:在线安装
			-1.切换root用户
			-2.使用yum命令:yum install -y nc
				安装成功之后,退出root,exit
	-2.2nc安装结束之后,验证是否成功
		命令:nc -lk 99999
		
	-2.3开始编写代码
代码:
package com.bigdata.SparkStreaming

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

object SparkStreamingSocketDemo {

  def main(args: Array[String]): Unit = {

    //1.构建上下文 ==》 StreamingContext 基于 SparkContext
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("SparkStreamingSocketDemo")

    val sc = SparkContext.getOrCreate(config)

    //def this(sparkContext: SparkContext, batchDuration: Duration)
    //通过查看源码可知,构建StreamingContext需要传这两个参数
    //batchDuration:用户指定的批次产生的间隔时间
    val ssc  = new StreamingContext(sc,Seconds(5))

    //2.读取数据形成DStream
/*    def socketTextStream(
                          hostname: String,
                          port: Int,
                          storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
                        )*/
    val dstream: ReceiverInputDStream[String] = ssc.socketTextStream("superman-bigdata.com",9999)

    //3.数据处理,计算词频统计
    //hello \t spark \t hbase
    val result: DStream[(String, Int)] = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word,1))
      .reduceByKey(_ + _)

    //4.数据的输出
    result.print()

    //5.开启流式程序,如果上一个批次没有计算结束,那么下一个批次处于阻塞
    ssc.start()
    ssc.awaitTermination()

  }
}
	-2.4先在linux中执行以下命令:
		nc -lk 9999 回车
	-2.5运行IDEA中的代码
注意:如果你觉得代码在运行的时候,控制台的日志过多,导致看不清
你可以调整日志等级
	-1.修改项目中的log4j文件
		log4j.rootCategory=ERROR,console
	-2.直接编写代码修改等级
		sc.setLogLevel("ERROR")
	
=================================================================
SparkStreaming程序的运行原理:
	两种数据的接受方式:
		第一种方式:使用数据接收器的方式(receiver)
			1.数据接收器接收的输入的数据,按照给定批次产生的间隔时间,然后产生一个一个的批次,批次内部是以block块的形式进行保存(保存在内存或者磁盘,缓存等级决定内存还是磁盘)。
			block块的产生也是基于时间的。默认每200ms形成一个block块。
			我们可以使用这个参数来修改block块产生的时间隔间:
			spark.streaming.blockInterval = 200ms
			spark的应用参数,可以写在三个地方:
				-1.写在spark-default.conf文件中(永久生效,一般写的是通用参数)
				-2.写在代码中,SparkConf下面 (每个程序中才会生效,一次性的)
				-3.在执行bin/spark-submit的命令后面加上参数 (提交的应用程序才会生效,一次性的)
			
			而且批次就是一个RDD,block块其实就是分区。
			比如:批次产生的间隔时间是5秒钟,那么默认情况下,每个批次中有25个block块
			也就是说当前RDD中存在25个分区。那么通过之前学习的SparkCore,我们可以知道一个分区其实就是一个task任务,那么批次中有多少个block块,其实就有多少个task任务。
			
			注意:
				-1.在receiver的模式下,block的个数的确是当前批次的分区数。但是其实只有存在block的时候,才会有分区,也就是说只有数据源源不断的接受,才会形成稳定的分区数。如果没有数据,那么分区数就是0,如果数据不多,而且不稳定,那么分区数是在变化的
				
				-2.基于receiver数据接收器的模式下,数据接受和数据处理其实是分开的,是不同的线程计算的。必须要有一个独立的线程负责接收数据,所以receiver模式下,线程数必须要大于2
				
				不管计算的批次是否阻塞,接受数据的线程永远会按照给定的时间间隔产生对应的批次,所以接受器的线程和计算的线程是相对独立的,互不影响的
				
			2.bacth和RDD的产生
				每隔一个批次产生的时间就会产生一个批次,这个批次中的数据,就是这段时间内接收器接收到的数据
				每个批次实质上就是一个RDD
				一个批次对应一个RDD
				一个批次中的block对应就是RDD中的分区
				所以block的个数,就是RDD的分区数。
			
		第二种方式:直接读取数据(direct)
			使用direct模式,每隔一个批次的时间,产生一个批次,但是因为没有数据接收器,所以也就不存在block。直接将数据的元数据信息,保存在RDD中。RDD的分区数是由数据源的分区数决定的
			比如:sparkstreaming使用direct模式,接受kafka中的beifeng0topic中的数据,那么每个批次也就是每个RDD的分区数应该是topic的分区数。
请注意:不管是receiver模式,还是direct模式,我们说的分区数都是第一次形成DStream流的时候的分区数
如果这条流已经开始计算了,那么分区数就会发生改变,而不是我们以上说的情况了

=================================================================
DStream(Discretized Streams)(离散化的流)
	DStream底层其实就是一系列的RDD和时间组成的集合流 (微批模拟流)
DStream有三大特性:
* A DStream internally is characterized by a few basic properties:

*  - A list of other DStreams that the DStream depends on
		DStream之间是相互依赖的
*  - A time interval at which the DStream generates an RDD
		DStream会间隔性的产生RDD(其实:每隔一个批次产生的时间,就会产生一个RDD)
*  - A function that is used to generate an RDD after each time interval
		会有一个专门函数:compute
		
=======================================================================================
Input DStream输入流,也就是SparkStreaming的数据源
官方文档:
	http://spark.apache.org/docs/2.2.1/streaming-programming-guide.html#input-dstreams-and-receivers

Spark Streaming provides two categories of built-in streaming sources.
SparkStreaming提供了内置的两种数据源
	
1.Basic sources(基础源): Sources directly available in the StreamingContext API. Examples: file systems, and socket connections.
这些源一般都是在StreamingContextAPI下面自带的一些方法。
比如以下API就是SparkStreaming的基础源(org.apache.spark.streaming.StreamingContext下的一些读取数据的方法)
-1.def socketTextStream(
      hostname: String,
      port: Int,
      storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
    ): ReceiverInputDStream[String]
	
-2.def socketStream[T: ClassTag](
      hostname: String,
      port: Int,
      converter: (InputStream) => Iterator[T],
      storageLevel: StorageLevel
    ): ReceiverInputDStream[T]
	
-3.def rawSocketStream[T: ClassTag](
      hostname: String,
      port: Int,
      storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
    ): ReceiverInputDStream[T]
	
-4.def fileStream[
    K: ClassTag,
    V: ClassTag,
    F <: NewInputFormat[K, V]: ClassTag
  ] (directory: String): InputDStream[(K, V)] 
  
以上采用的都是基于数据接收器的

Advanced sources(额外的数据源): Sources like Kafka, Flume, Kinesis, etc. are available through extra utility classes. These require linking against extra dependencies as discussed in the linking section.
	
	Kafka: Spark Streaming 2.2.1 is compatible with Kafka broker versions 0.8.2.1 or higher. See the Kafka Integration Guide for more details.

	Flume: Spark Streaming 2.2.1 is compatible with Flume 1.6.0. See the Flume Integration Guide for more details.
	
===================================================================================================
Kafka和SparkStreaming的集成:
	官方集成指南:spark.apache.org/docs/2.2.1/streaming-kafka-integration.html

问题:	
	SparkStreaming的版本是2.2.1,Kafka的版本是0.11.0.1
	那么,SparkStreaming和Kafka集成肯定是需要集成包的
	集成包使用什么版本呢?
SparkStreaming和Kafka的集成包有两个版本:
spark-streaming-kafka-0-8,spark-streaming-kafka-0-10
这两个版本,我们都要讲。

===================================================================================================
SparkStreaming和Kafka的集成包:spark-streaming-kafka-0-8
文档地址:http://spark.apache.org/docs/2.2.1/streaming-kafka-0-8-integration.html
集成方式有两种:
	1.基于数据接收器的receiver模式
	2.直接读取数据的模式
	
1.基于数据接收器的receiver模式
第一步:在pom文件中添加依赖 
	<dependency>
	  <groupId>org.apache.spark</groupId>
	  <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
	  <version>2.2.1</version>
	</dependency>

第二步:选择输入源的API,编写代码 使用KafkaUtils类中的方法创建流
def createStream(
  ssc: StreamingContext, //上下文
  zkQuorum: String,  //zookeeper的集群地址以及元数据保存的目录
  groupId: String, 消费者的组ID
  topics: Map[String, Int], 待读取的topic以及消费topic需要的线程数
  storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
): ReceiverInputDStream[(String, String)]

代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

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

object Receiver01 {
  def main(args: Array[String]): Unit = {
    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Receiver01")
      .set("spark.streaming.blockInterval","1000")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(5))

    //2.读取数据形成DStream  => 使用receiver模式,这个代码是写死的,无法传入kafka的消费者的配置参数
    //这个代码的消费者的偏移量是直接保存在zookeeper里面的
    val zkQuorum = "superman-bigdata.com:2181/yangpu1005"
    val groupId = "xiaoming"
    val topics = Map(
      "receiver" -> 4
    )

    val dstream: DStream[String] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics).map(t => t._2)
    dstream.foreachRDD(rdd => {
      println(rdd.partitions.size)
    })

    //3.计算数据
    val result = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word, 1))
      .reduceByKey((a, b) => a + b)

    //4.结果输出
    result.print()

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()

  }
}

def createStream[K: ClassTag, V: ClassTag, U <: Decoder[_]: ClassTag, T <: Decoder[_]: ClassTag](
  ssc: StreamingContext,  //上下文
  kafkaParams: Map[String, String], //kafka消费者的配置参数
  topics: Map[String, Int], //待读取的topic以及消费topic需要的线程数
  storageLevel: StorageLevel //缓存等级
): ReceiverInputDStream[(K, V)] 
代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import kafka.serializer.StringDecoder
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Receiver02 {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Receiver01")
      .set("spark.streaming.blockInterval","1000")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(5))

    //2.读取数据形成DStream  ==》 使用receiver模式
    //报错:Wrong value earliest of auto.offset.reset in ConsumerConfig; Valid values are smallest and largest
    //earliest和latest这个是新版本中的参数值  老版本中的应该是smallest and largest
    val kafkaParams = Map(
      "zookeeper.connect" -> "superman-bigdata.com:2181/yangpu1005",
      "group.id" -> "xiaohong",
      "auto.offset.reset" -> "smallest"
    )
    val topics = Map(
      "receiver" -> 4
    )
    val storageLevel =StorageLevel.MEMORY_AND_DISK
    val dstream: DStream[String] = KafkaUtils.createStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics,storageLevel).map(t => t._2)

    //3.计算数据
    val result = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word, 1))
      .reduceByKey((a, b) => a + b)

    //4.结果输出
    result.print()

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}

===================================================================================================
2.直接读取数据的模式(Direct)
注意:direct模式下,偏移量是不会直接保存到zookeepr中的
第一步:在pom文件中添加依赖 
	<dependency>
	  <groupId>org.apache.spark</groupId>
	  <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
	  <version>2.2.1</version>
	</dependency>

第二步:选择输入源的API,编写代码 使用KafkaUtils类中的方法创建流
def createDirectStream[
K: ClassTag,
V: ClassTag,
KD <: Decoder[K]: ClassTag,
VD <: Decoder[V]: ClassTag] (
  ssc: StreamingContext, //上下文 
  kafkaParams: Map[String, String], //kafka消费者的配置参数
  topics: Set[String] //待消费的topic
): InputDStream[(K, V)]
代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import kafka.serializer.StringDecoder
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Direct01 {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Direct01")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(5))

    //2.读取数据形成DStream ==> Direct模式是不需要连接zookeeper。偏移量不会写入zookeeper中,而且也不会做任何保存操作
   //所以当我们使用direct的时候,需要自己去保存偏移量信息
    val kafkaParams = Map(
      "metadata.broker.list" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "xiaohei",
      "auto.offset.reset" -> "largest" //默认从最新开始消费  largest
    )
    val topics = Set("direct")

    val dstream: DStream[String] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics).map(t => t._2)
    //我们说direct模式下,第一次的时候,dstream中的分区数应该和topic的分区数一致
    //我们知道direct这个topic的分区数我设置的是4个
    //那么下面代码打印的应该也是4
    dstream
      .foreachRDD(rdd => {
        println(rdd.partitions.size)
      })

    //3.计算数据
    val result = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word, 1))
      .reduceByKey((a, b) => a + b)

    //4.结果输出
    result.print()

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}

def createDirectStream[
K: ClassTag,
V: ClassTag,
KD <: Decoder[K]: ClassTag,
VD <: Decoder[V]: ClassTag,
R: ClassTag] (
  ssc: StreamingContext, //上下文
  kafkaParams: Map[String, String], //kafka消费者的配置参数
  fromOffsets: Map[TopicAndPartition, Long], //指定消费的topic,以及指定从哪一个分区的哪一个偏移量位置开始消费
  messageHandler: MessageAndMetadata[K, V] => R //数据以及元数据信息
): InputDStream[R] 
代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import kafka.common.TopicAndPartition
import kafka.message.MessageAndMetadata
import kafka.serializer.StringDecoder
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Direct02 {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Direct02")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(5))

    //2.读取数据形成DStream
    val kafkaParams = Map(
      "metadata.broker.list" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "xiaobai"
    )

    val fromOffsets = Map(
      //1.可以只写一个或者几个分区 2.指定的偏移量的值不可以超过当前分区的数据条数
      //报错:java.lang.IllegalArgumentException: requirement failed:  numRecordsmust not be negative
      //这个报错就是因为指定的偏移量超过了当前分区的数据条数,导致的
      TopicAndPartition("direct",0) -> 2L,
      TopicAndPartition("direct",1) -> 2L,
      TopicAndPartition("direct",2) -> 3L,
      TopicAndPartition("direct",3) -> 3L
    )

    val messageHandler: MessageAndMetadata[String, String] => String = (msg:MessageAndMetadata[String,String]) => {
      val topicName = msg.topic
      val partitionID = msg.partition
      val offset = msg.offset
      val value = msg.message()

      val sb = new StringBuilder
      sb.append("topicName=").append(topicName).append(",")
        .append("partitionID=").append(partitionID).append(",")
        .append("offset=").append(offset).append(",")
        .append("value=").append(value)
     sb.toString()
    }

    val dstream = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder,String](ssc,kafkaParams,fromOffsets,messageHandler)

    //3.结果输出
    dstream.print()

    //4.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}

//因为kafka的direct模式下,不会对消费者的偏移量做什么保存操作,因此,同一个消费者的情况下,我们无法确定上一次消费的记录,所以我们接下来学习手动保存偏移量记录,保存到mysql数据库中
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import java.sql.{Connection, DriverManager}

import kafka.common.TopicAndPartition
import kafka.message.MessageAndMetadata
import kafka.serializer.StringDecoder
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Direct02 {
  def main(args: Array[String]): Unit = {
    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Direct02")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(5))

    //2.读取数据形成DStream
    val kafkaParams = Map(
      "metadata.broker.list" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "xiaobai"
    )

    val fromOffsets = Map(
      //1.可以只写一个或者几个分区 2.指定的偏移量的值不可以超过当前分区的数据条数
      //报错:java.lang.IllegalArgumentException: requirement failed:  numRecordsmust not be negative
      //这个报错就是因为指定的偏移量超过了当前分区的数据条数,导致的
      TopicAndPartition("direct",0) -> 2L,
      TopicAndPartition("direct",1) -> 2L,
      TopicAndPartition("direct",2) -> 3L,
      TopicAndPartition("direct",3) -> 3L
    )

    val messageHandler = (msg:MessageAndMetadata[String,String]) => {
      val topicName = msg.topic
      val partitionID = msg.partition
      val offset = msg.offset
      val value = msg.message()

      //偏移量记录插入数据库
      insertOffsetIntoMysql(topicName,partitionID,offset)

      val sb = new StringBuilder
      sb.append("topicName=").append(topicName).append(",")
        .append("partitionID=").append(partitionID).append(",")
        .append("offset=").append(offset).append(",")
        .append("value=").append(value)
     sb.toString()
    }

    val dstream = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder,String](ssc,kafkaParams,fromOffsets,messageHandler)

    //3.结果输出
    dstream.print()

    //4.开启程序
    ssc.start()
    ssc.awaitTermination()
  }

  def insertOffsetIntoMysql(topicName:String,partitionID:Int,offset:Long)={

    //1.使用JDBC原生态的方式连接数据库
    val url = "jdbc:mysql://superman-bigdata.com:3306/yangpu1005"
    val user = "root"
    val password = "123456"
    var connect = DriverManager.getConnection(url,user,password)

    //插入的sql语句
    val sql = "replace into ManualOffsetConsumer values (?,?,?)"
    var pstmt = connect.prepareStatement(sql)

    pstmt.setString(1,topicName)
    pstmt.setInt(2,partitionID)
    pstmt.setLong(3,offset)
    pstmt.executeUpdate()

    pstmt.close()
    connect.close()
  }

}

===================================================================================================
SparkStreaming和Kafka集成的代码优化:
1.receiver模式下的优化方式:
	-1.设置分区数,我们知道数据量越大,分区数要越大,那么在receiver模式下,如何调整分区数
		控制block块的个数:spark.streaming.blockInterval 默认200ms 注意,这个值必须是批次间隔时间的整数倍
	-2.当发现接受的速率比较慢,可以考虑给定多个数据接收器
		比如:
			val dstream1: DStream[String] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics).map(t => t._2)
			val dstream2: DStream[String] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics).map(t => t._2)
			val dstream3: DStream[String] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics).map(t => t._2)
			val dstream4: DStream[String] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics).map(t => t._2)
    
			val dstream = dstream1.union(dstream2).union(dstream3).union(dstream4)
		
	!!-3.当接受的速度太快了,根本来不及计算。产生严重的延迟情况,甚至导致程序失败
			使用背压机制来解决这个问题
			spark.streaming.backpressure.enabled = true  开启背压机制,默认是关闭的
			spark.streaming.receiver.maxRate 控制每秒每个分区中接受的最大的处理条数
			例子:val config = new SparkConf()
				  .setMaster("local[*]")
				  .setAppName("Receiver01")
				  .set("spark.streaming.blockInterval","1000")
				  .set("spark.streaming.backpressure.enabled","true" )
				  .set("spark.streaming.receiver.maxRate","5") //每秒每个分区中处理5条数据
				  

2.direct模式下的优化方式
	-1.不管你是receiver还是direct模式下,都可以开启的
		开启动态资源分配
			使用场景:当程序出现job的高峰和低谷的时候,动态的控制executor的数量
			spark.dynamicAllocation.enabled 默认是false 设置为true 开启动态资源调度
			spark.dynamicAllocation.initialExecutors 表示executor的初始的个数
			spark.dynamicAllocation.maxExecutors 表示executor浮动变化的最大的个数
			spark.dynamicAllocation.minExecutors 表示executor浮动变化的最小的个数
	
	-2.当接受的速度太快了,根本来不及计算。产生严重的延迟情况,甚至导致程序失败
			使用背压机制来解决这个问题
			spark.streaming.backpressure.enabled = true  开启背压机制,默认是关闭的
			spark.streaming.kafka.maxRatePerPartition  每个分区每秒钟处理的最大的数据条数,不设置表示没有任何的限制
			例子:    val config = new SparkConf()
						  .setMaster("local[2]")
						  .setAppName("Direct01")
						  .set("spark.streaming.backpressure.enabled","true")
						  .set("spark.streaming.kafka.maxRatePerPartition","3")
		
===================================================================================================
!!!!!!检查点机制:checkpoint
官方文档:	
	http://spark.apache.org/docs/2.2.1/streaming-programming-guide.html#checkpointing
	作用:当程序还没有完全执行结束,程序突然中止了。那么我们再次提交程序的时候,我们希望程序不是从头开始执行的,而是从上一次结束的地方继续运行,那么我们可以使用检查点机制,来进行应用的恢复。
例子:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import kafka.serializer.StringDecoder
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.DStream
import org.apache.spark.streaming.kafka.KafkaUtils

object Checkpoint {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[2]")
      .setAppName("Direct01")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(3))

    //设置检查点机制
    val path = "hdfs://superman-bigdata.com:9000/yangpu1005/checkpoint"
    ssc.checkpoint(path)

    //2.读取数据形成DStream ==> Direct模式是不需要连接zookeeper。偏移量不会写入zookeeper中,而且也不会做任何保存操作
    //所以当我们使用direct的时候,需要自己去保存偏移量信息
    val kafkaParams = Map(
      "metadata.broker.list" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "hello",
      "auto.offset.reset" -> "smallest" //默认从最新开始消费  largest
    )
    val topics = Set("direct")

    val dstream: DStream[String] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics).map(t => t._2)

    //3.计算数据
    val result = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word, 1))
      .reduceByKey((a, b) => a + b)

    //4.结果输出
    result.print()

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()

  }
}

===================================================================================================
Direct模式下,因为偏移量是不做任何保存操作的,所以我们不知道怎么去查看之前消费者的偏移量记录
所以我们必须手动进行偏移量的保存
在direct模式下,我们可以使用一下两种方式保存偏移量
-1.def createDirectStream[
K: ClassTag,
V: ClassTag,
KD <: Decoder[K]: ClassTag,
VD <: Decoder[V]: ClassTag,
R: ClassTag] (
  ssc: StreamingContext, //上下文
  kafkaParams: Map[String, String], //kafka消费者的配置参数
  fromOffsets: Map[TopicAndPartition, Long], //指定消费的topic,以及指定从哪一个分区的哪一个偏移量位置开始消费
  messageHandler: MessageAndMetadata[K, V] => R //数据以及元数据信息
): InputDStream[R] 
我们可以在messageHandler对象中进行偏移量的保存,保存到mysql中


-2.def createDirectStream[
K: ClassTag,
V: ClassTag,
KD <: Decoder[K]: ClassTag,
VD <: Decoder[V]: ClassTag] (
  ssc: StreamingContext, //上下文 
  kafkaParams: Map[String, String], //kafka消费者的配置参数
  topics: Set[String] //待消费的topic
): InputDStream[(K, V)]
这种代码呢,不能执行在以上代码中确定偏移量的保存
但是可以直接使用DStream转成RDD,来获取每个RDD中元数据信息来进行保存

栗子:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_8

import kafka.serializer.StringDecoder
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka.{HasOffsetRanges, KafkaUtils, OffsetRange}
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Direct_SaveOffset_Common {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[2]")
      .setAppName("Direct_SaveOffset_Common")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc, Seconds(1))

    //2.读取数据形成DStream
    val kafkaParams = Map(
      "metadata.broker.list" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "xiaoxing",
      "auto.offset.reset" -> "smallest" //默认从最新开始消费  largest
    )
    val topics = Set("direct")

    val dstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)
    //报错:java.lang.ClassCastException: org.apache.spark.rdd.MapPartitionsRDD cannot be cast to org.apache.spark.streaming.kafka.HasOffsetRanges
    //表示类型不一致导致的,我们不需要取出value的值,直接保存原始的流


    //3.结果数据
    dstream.map(t => t._2).print()

    //4.偏移量的手动保存
    //构建一个数组进行偏移量的保存
    var  offsetRanges: Array[OffsetRange] =  Array.empty
    dstream
      .foreachRDD(rdd  => {
        offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges //得到的当前RDD中所有数据的偏移量的范围

        val arr: Array[OffsetRange] = offsetRanges.toSet.toArray

        for(msg <- arr){
          val topicName = msg.topic
          val partitionID = msg.partition
          val fromOffsets = msg.fromOffset
          val untilOffsets = msg.untilOffset

          println("topicName=" + topicName + "," + "partitionID=" + partitionID + "," + "fromOffsets=" + fromOffsets + "," + "untilOffsets=" + untilOffsets )
          //作业:使用原生态的JDBC代码自己写入数据库中
        }
      })

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}

===================================================================================================
SparkStreaming和Kafka的集成包:spark-streaming-kafka-0-10
官方文档:
	http://spark.apache.org/docs/2.2.1/streaming-kafka-0-10-integration.html

-1.在pom文件中添加依赖
    <dependency>
      <groupId>org.apache.spark</groupId>
      <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
      <version>2.2.1</version>
    </dependency>
				
-2.注意:spark-streaming-kafka-0-10在这个集成包的下面
sparkstreaming和kafka集成只有一种方式,那就是direct模式
而且这个版本的消费者偏移量和zookeeper没有任何关系!!!!!

在这个版本下:
消费者的偏移量管理有两种方式:
	-1.偏移量自动提交,保存在本地
		"enable.auto.commit" -> "true" 表示偏移量是程序自动提交在本地保存的
		
	-2.手动管理偏移量
		"enable.auto.commit" -> "false" 表示消费者的偏移量是没有任何保存的,必须要手动去储存偏移量
案例:
-1.偏移量自动提交,保存在本地
"enable.auto.commit" -> "true" 表示偏移量是程序自动提交在本地保存的
def createDirectStream[K, V](
  ssc: StreamingContext,
  locationStrategy: LocationStrategy,
  consumerStrategy: ConsumerStrategy[K, V]
): InputDStream[ConsumerRecord[K, V]] 
代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_10

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.{SparkConf, SparkContext}

object Auto_Offset_Commit {

  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Auto_Offset_Commit")

    val sc = SparkContext.getOrCreate(config)

    val ssc = new StreamingContext(sc,Seconds(5))

    //2.读取数据形成DStream
    //报错:org.apache.kafka.common.config.ConfigException:
    // Missing required configuration "key.deserializer" which has no default value.

    val topics = Array("direct")
    val kafkaParams = Map(
      "bootstrap.servers" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "zhangsan", //消费者组ID
      "enable.auto.commit" -> "true", //表示偏移量自动保存
      "auto.offset.reset" -> "earliest",  // 从最新还是最老开始消费  earliest /lagest
      "key.deserializer" -> classOf[StringDeserializer], //key的序列化
      "value.deserializer" -> classOf[StringDeserializer] //value的序列化
    )

    val dstream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String,String](ssc,PreferConsistent,Subscribe[String,String](topics,kafkaParams))

    val result: DStream[(String, Int, Long)] = dstream
      .map(record => {
        val topicName = record.topic()
        val partitionID = record.partition()
        val offset = record.offset()
        val value = record.value()
        (topicName,partitionID,offset)
      })

    //3.打印结果
    result.print()

    //4.开始运行程序
    ssc.start()
    ssc.awaitTermination()

  }
}

-2.手动管理偏移量
"enable.auto.commit" -> "false" 表示消费者的偏移量是没有任何保存的,必须要手动去储存偏移量		
代码:
package com.bigdata.SparkStreaming.Kafka_Streaming_0_10

import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.common.TopicPartition
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka010.{HasOffsetRanges, OffsetRange}
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.{SparkConf, SparkContext, TaskContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Manual_Offset_Commit {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("Manual_Offset_Commit")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc,Seconds(5))

    //2.读取数据形成DStream
    val topics = Array("direct")

    val kafkaParams = Map(
      "bootstrap.servers" -> "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095",
      "group.id" -> "lisi", //消费者组ID
      "enable.auto.commit" -> "false", //表示偏移量不自动保存
      "key.deserializer" -> classOf[StringDeserializer], //key的序列化
      "value.deserializer" -> classOf[StringDeserializer] //value的序列化
    )

    val offset = Map(
      new TopicPartition("direct",0) -> 2L,
      new TopicPartition("direct",1) -> 2L,
      new TopicPartition("direct",2) -> 3L,
      new TopicPartition("direct",3) -> 3L
    )

    val dstream: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream[String,String](ssc,PreferConsistent,Subscribe[String,String](topics,kafkaParams,offset))

    val result: DStream[(String, Int, Long)] = dstream
      .map(record => {
          val topicName = record.topic()
          val partitionID = record.partition()
          val offset = record.offset()
          val value = record.value()
    (topicName,partitionID,offset)
  })

    //3.打印结果
    result.print()

    //4.进行手动的储存消费者的偏移量信息
    dstream
      .foreachRDD(rdd => {
        val offsetRanges: Array[OffsetRange] = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
        rdd.foreachPartition(iter => {
            val o: OffsetRange = offsetRanges(TaskContext.get().partitionId())
          println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")

          //作业:使用原生态的JDBC代码,写入数据库中
        })
      })

    //5.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}
	
===================================================================================================			
SparkStreaming的一些特殊的方法:
	1.transform
	2.foreachRDD
	3.updateStateByKey
	4.Window

1.transform =》 转换函数
transform这个函数用于将DStream转成RDD,然后使用RDD的api进行数据处理,处理完之后,仍然返回DStream
一般用于数据转换
代码:
package com.bigdata.SparkStreaming

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

object SparkStreaming_Transform {

  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("SparkStreaming_Transform")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val spark = SparkSession.builder().master("local[*]").appName("SparkStreaming_Transform").getOrCreate()

    val ssc = new StreamingContext(sc,Seconds(5))

    //2.读取数据形成DStream
    val dstream: ReceiverInputDStream[String] =  ssc.socketTextStream("superman-bigdata.com",9999)

    //3.使用transform
    val result1  = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word,1))
      .reduceByKey((a,b) => a + b)

    val result2: DStream[(String, Int)] = dstream
      .transform(rdd => {
        val rdd1: RDD[(String, Int)] = rdd
          .flatMap(line => line.split("\t"))
          .filter(word => word.nonEmpty)
          .map(word => (word,1))
          .reduceByKey((a,b) => a + b)

        //将RDD转成DataFrame
        import  spark.implicits._
        val df = rdd1.toDF("word","count")
        df.show()
        rdd1
      })

    result1.print()
    result2.print()

    //4.开启程序
    ssc.start()
    ssc.awaitTermination()
  }
}

2.foreachRDD  ==》 数据输出
这个函数也是将DStream转成RDD ,但是一般用在数据的输出
代码:
package com.bigdata.SparkStreaming

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

object SparkStreaming_ForeachRDD {
  def main(args: Array[String]): Unit = {

    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("SparkStreaming_ForeachRDD")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val spark = SparkSession.builder().master("local[*]").appName("SparkStreaming_ForeachRDD").getOrCreate()

    val ssc = new StreamingContext(sc,Seconds(5))

    //2.读取数据形成DStream
    val dstream: ReceiverInputDStream[String] =  ssc.socketTextStream("superman-bigdata.com",9999)

    //3.使用foreachRDD
    dstream
      .foreachRDD(rdd => {
        val rdd1: RDD[(String, Int)] = rdd
          .flatMap(line => line.split("\t"))
          .filter(word => word.nonEmpty)
          .map(word => (word,1))
          .reduceByKey((a,b) => a + b)

        import spark.implicits._
        val df = rdd1.toDF("word","count")
        df.show()
        df.write.mode("append").format("json").save("hdfs://superman-bigdata.com:9000/yangpu1005/foreachRDD")
      })

    ssc.start()
    ssc.awaitTermination()
  }
}

===================================================================================================
3.updateStateByKey
应用场景:实时累计功能:
报错:java.lang.IllegalArgumentException: requirement failed: The checkpoint directory has not been set. Please set it by StreamingContext.checkpoint().
表示没有设置checkpiont(元数据保存)
为什么使用updateStateByKey的时候,需要设置checkpoint机制????
1.checkpoint是用来保存之前批次的所有的元数据信息
2.普通的sparkstreaming程序,只能获得当前批次的信息
所以我们必须要设置checkpoint,因为程序需要知道之前的批次的运行状态值

代码:
package com.bigdata.SparkStreaming

import java.util.Properties

import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.{HTable, Put, Result}
import org.apache.hadoop.hbase.io.ImmutableBytesWritable
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat
import org.apache.hadoop.hbase.util.Bytes
import org.apache.hadoop.mapreduce.Job
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.SparkSession
import org.apache.spark.streaming.dstream.ReceiverInputDStream
import org.apache.spark.streaming.{Seconds, StreamingContext}

object SparkStreaming_UpdateStateByKey {

  def main(args: Array[String]): Unit = {
    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("SparkStreaming_UpdateStateByKey")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val spark = SparkSession.builder().master("local[*]").appName("SparkStreaming_UpdateStateByKey").enableHiveSupport().getOrCreate()

    val ssc = new StreamingContext(sc,Seconds(5))

    //checkpoint 检查点机制
    val path = "hdfs://superman-bigdata.com:9000/yangpu1005/checkpoint"
    ssc.checkpoint(path)

    //2.读取数据形成DStream
    val dstream: ReceiverInputDStream[String] =  ssc.socketTextStream("superman-bigdata.com",9999)

    //3.使用updateStateByKey实现实时累计效果
    //  def updateStateByKey(updateFunc: (Seq[V], Option[S]) => Option[S]): DStream[(K, S)]
    //seq:Seq[V]:当前批次的数据
    //state:Option[S]:上一个批次的状态值
    val result  = dstream
      .flatMap(line => line.split("\t"))
      .filter(word => word.nonEmpty)
      .map(word => (word,1))
      .reduceByKey((a,b) => a + b)  //(word,count)
      .updateStateByKey(
      (seq:Seq[Int],state:Option[Int]) => {
        //seq:表示当前批次的key的value值
        //state:表示上一个批次的key的value的状态值
        val currentValue = seq.sum
        val preValue = state.getOrElse(0)
        Some(currentValue + preValue)
      }
    )


    //4.结果输出打印
    result.print()

    //5.将结果保存到外部储存系统
    //5.1保存到本地windows的文件系统
    result.saveAsTextFiles("file:///D:\\data\\updateStateByKey")

    //5.2保存到HDFS的分布式文件系统
    result.saveAsTextFiles("hdfs://superman-bigdata.com:9000/yangpu1005/updateStateByKey")

    //5.3保存到hive表中
    result.foreachRDD(rdd => {
      import spark.implicits._
      rdd.toDF("word","count")
        .write
        .mode("overwrite")
        .format("json")
        .saveAsTable("yangpu1005.updateStateByKey")
    })

    //5.4保存到mysql中
    result.foreachRDD(rdd => {
      import spark.implicits._
      val url = "jdbc:mysql://superman-bigdata.com:3306/yangpu1005"
      val table = "updateStateByKey"
      val props = new Properties()
      props.put("user","root")
      props.put("password","123456")
      rdd.toDF("word","count")
        .write
        .mode("overwrite")
        .format("json")
        .jdbc(url,table,props)
    })

    //5.5保存到hbase表中
    //方式一:将DStream转成RDD,然后使用RDD的foreachPartition写入hbase中
    result
        .foreachRDD(rdd => {
          rdd.foreachPartition(iter => {
            //1.获取hbase的配置信息
            val config  = HBaseConfiguration.create()
            //2.获取hbase的表对象,首先在hbase中建表 create 'updateStateByKey','info'
            val table  = new HTable(config,"updateStateByKey")
            //3.构建put对象
            iter.foreach(t => {
              val word = t._1
              val count = t._2
              val put = new Put(Bytes.toBytes(word))
              put.add(Bytes.toBytes("info"),Bytes.toBytes("count"),Bytes.toBytes(count))
              table.put(put)
            })
            table.close()
          })
        })

    //方法二:使用org.apache.spark.rdd.PairRDDFunctions中的saveAsNewAPIHadoopDataset写到hbase中
/*    def saveAsNewAPIHadoopDataset(conf: Configuration): Unit = self.withScope {
      SparkHadoopMapReduceWriter.write(
        rdd = self,
        hadoopConf = conf)
    }*/
    result
        .foreachRDD(rdd => {
          //1.设置配置参数
          val sc = rdd.sparkContext
          sc.hadoopConfiguration.set("hbase.zookeeper.quorum","superman-bigdata.com:2181") //zk的连接地址
          sc.hadoopConfiguration.set("hbase.rootdir","hdfs://superman-bigdata.com:9000/hbase")
          sc.hadoopConfiguration.set("hbase.master","superman-bigdata.com:60000")
          //以上的我们这边可以不需要写,因为我们已经把hbase-site.xml文件放在项目中
          sc.hadoopConfiguration.set(TableOutputFormat.OUTPUT_TABLE,"updateStateByKey") //待输入的hbase的表
          //2.构建job对象
          val job = Job.getInstance(sc.hadoopConfiguration)
          job.setOutputKeyClass(classOf[ImmutableBytesWritable])
          job.setOutputValueClass(classOf[Result])
          job.setOutputFormatClass(classOf[TableOutputFormat[ImmutableBytesWritable]])
          rdd.saveAsNewAPIHadoopDataset(job.getConfiguration)
        })
    
    ssc.start()
    ssc.awaitTermination()

  }
}

===================================================================================================
4.Window 窗口  不是窗口函数!!!!
应用场景:计算最近一段时间的数据
例子:
批次间隔的时间是5s 计算的结果是每5s内接收到的数据结果
计算最近30s的数据结果,相当于计算最近6个批次的数据

窗口的执行过程:
需求:每隔20s,打印最近30s的数据
批次产生的间隔时间是10s
窗口的大小也就是最近的一段时间:30s
窗口的滑动时间也就是新窗口产生的间隔时间:20s
12:00:00   12:00:10   12:00:20     12:00:30     12:00:40   。。。。。
			批次1       批次2       批次3         批次4   。。。
			            window1                   window2  。。。
12:00:20
进行第一次的打印:window1 =  批次1 + 批次2 

12:00:40
进行第二次的打印:window2 =  window1 + 批次3 + 批次4 - 批次1 = 批次2 + 批次3 + 批次4

。。。。。
规则:
	window = 上一个window + 新window产生的那段时间得到的新的批次 - 在上一个window中和当前window不重叠的部分
	
	
在使用Window的时候,代码中需不需要设置checkpoint机制???

代码:
package com.bigdata.SparkStreaming

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

object SparkStreaming_Window {

  def main(args: Array[String]): Unit = {
    //1.构建上下文
    val config = new SparkConf()
      .setMaster("local[*]")
      .setAppName("SparkStreaming_Window")

    val sc = SparkContext.getOrCreate(config)
    sc.setLogLevel("ERROR")

    val ssc = new StreamingContext(sc,Seconds(10))

    //checkpoint 检查点机制
    val path = "hdfs://superman-bigdata.com:9000/yangpu1005/checkpoint"
    ssc.checkpoint(path)

    //2.读取数据形成DStream
    val dstream: ReceiverInputDStream[String] =  ssc.socketTextStream("superman-bigdata.com",9999)

    //3.使用window进行计算:需求:每隔20s,打印最近30s的数据
/*    def reduceByKeyAndWindow(
                              reduceFunc: (V, V) => V, //聚合函数
                              invReduceFunc: (V, V) => V, //删除上一个window中不重叠的部分
                              windowDuration: Duration, 窗口的大小
                              slideDuration: Duration = self.slideDuration, 窗口滑动的时间
                            ): DStream[(K, V)]*/
    val result = dstream
      .flatMap(line => line.split("\t"))
      .map(word => (word,1))
      .reduceByKeyAndWindow(
        (a,b) => a + b, //上一个window + 新window产生的那段时间得到的新的批次
        (c,d) => c - d ,//上一个window + 新window产生的那段时间得到的新的批次 - 在上一个window中和当前window不重叠的部分
        Seconds(30), //windowDuration 窗口的大小  也就是最近一段时间
        Seconds(20) //slideDuration 窗口的滑动时间 也就是新窗口产生的间隔时间
      )

    result.print()

    ssc.start()
    ssc.awaitTermination()

  }
}

	
	



			











































	

	
	
	
	
	
	
	
	
	
	
	

### 回答1: Kafka和Spark Streaming是大数据领域中非常重要的技术,它们可以协同工作,实现实时数据处理和分析。Kafka是一个分布式的消息队列系统,可以高效地处理海量数据流,而Spark Streaming则是一个基于Spark的流处理框架,可以实现实时数据处理和分析。在学习Kafka和Spark Streaming时,需要掌握它们的基本概念、原理和使用方法,以及如何将它们结合起来实现实时数据处理和分析。同时,还需要了解Kafka和Spark Streaming的优缺点,以及如何优化它们的性能和可靠性。 ### 回答2: Kafka是一个高性能,可扩展的分布式消息系统。它通过将消息划分成一个或多个主题,然后将这些主题划分成一个或多个分区来进行实现。Kafka是由LinkedIn开发的,由Apache基金会进行管理。它的主要设计目标是支持分布式处理,如流处理和批处理等。Kafka通过使用Zookeeper来进行节点管理和故障转移,能够快速处理海量的数据。Kafka采用发布/订阅模式,支持多个消费者订阅同一个主题,每个消费者可以读取主题的所有分区数据,也可以选择读取其中的某个分区。 Spark Streaming是一个流处理框架,它能够利用Spark的分布式处理能力来对实时数据进行处理。Spark Streaming采用微批处理的方式,将实时数据流切片成一段一段的,并通过并行处理的方式进行计算。Spark Streaming的数据源可以是Kafka、Flume或者TCP sockets等。与Kafka相比,Spark Streaming更适合于需要进行实时计算的场景,例如:实时日志分析、实时推荐、实时风控等。同时,Spark Streaming还能够与Spark的批处理进行无缝对接,实现流处理与批处理的统一计算引擎。Spark Streaming支持机器学习、图计算等高级计算库,能够为用户提供更强大的计算能力。 学习Kafka和Spark Streaming的过程中,需要掌握Java、Scala等编程语言基础知识,并具备分布式系统的相关知识。对于Kafka,需要了解其基本概念、架构、API等,理解消息系统、发布/订阅模式、分区等内容。对于Spark Streaming,需要掌握其基本概念、流式计算流程、数据源等,同时也要熟悉Spark的RDD、DataFrame等相关知识。在学习中,需要结合实际项目,进行代码实践和调试,同时不断学习、思考和总结,以加深对Kafka和Spark Streaming的理解和应用。 ### 回答3: Kafka是由Apache SoftWare Foundation开发的一种分布式发布/订阅消息系统。其主要目的是为数据传输提供一种高吞吐量、低延迟的解决方案。Kafka提供了一种可靠的、持久化的、实时的数据传输方式,适用于众多的场景,如:日志收集、数据传输、消息系统等。 Kafka的特点: 1、高吞吐量:Kafka可以支持非常高的数据传输吞吐量,同时保持低延迟和高稳定性。 2、可扩展性:Kafka可以轻松扩展以适应更大的数据需求,并可以在运行时添加新的主题分区。 3、持久化:Kafka保证数据能够可靠地在分布式集群中传输,同时保证数据不会丢失或者被意外删除。 4、多样化的客户端:Kafka提供了多种语言的客户端接口,以满足不同的开发需求。 SparkStreaming 是由Apache Spark社区发展的一个实时数据处理框架。它用于将实时数据流分成小批处理,可以跨越不同的时间窗口进行计算。Spark Streaming提供了与Spark非常相似的编程模型,同时支持不同的输入源,包括社交媒体、传感器、消息队列等。 SparkStreaming的特点: 1、处理速度快:它可以支持毫秒级别的处理速度,并且可以在分布式系统中实现高吞吐量。 2、支持多种数据源:Spark Streaming可以从多种类型的数据源中读取数据,如HDFS、Flume、Kafka等。 3、编程简单:Spark Streaming提供了与Spark相似的编程模式,使得开发人员可以将Spark StreamingSpark整合在一起进行处理。 4、高容错性:Spark Streaming在分布式环境中实现了高可靠性和容错性,使得它可以支持大规模的实时数据处理需求。 总之,Kafka和Spark Streaming这两个工具是在大数据处理领域中非常重要的工具。它们可以很好地相互结合,支持大规模的实时数据处理和分析,进而为企业提供更好更快的数据处理方案。如果你对这两个技术感兴趣,可以从官方文档和教程开始学习,逐步掌握它们的定义、特点、应用场景和基本使用方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值