组件介绍:

Apache Kafka 是一个可扩展,高性能,低延迟的消息队列,允许我们像消息系统一样读取和写入数据。
Spark Streaming 是 Apache Spark 的一部分,是一个可扩展、高吞吐、容错的实时流处理引擎。使用 Scala 语言开发的,支持 Java API。

技术架构:

kafka数据量过大数据库插入 kafka数据接入_spark


Kafka有两类客户端,Producer(消息生产者的)和Consumer(消息消费者)。消息生产者将应用程序的流式数据发送到Kafka,Kafka集群中每个节点都有一个被称为broker的实例,负责缓存数据。Kafka中不同业务系统的消息可通过topic进行区分,每个消息都会被分区,用以分担消息读写负载,每个分区又可以有多个副本来防止数据丢失。消费者在具体消费某个topic消息时,指定起始偏移量。

kafka数据量过大数据库插入 kafka数据接入_数据_02


SparkStreaming读取Kafka数据源有两种模式:

模式一:SparkStreaming 基于Receiver接收Kafka数据流,Receiver是使用Kafka的高层次Consumer API来实现的。receiver从Kafka中获取的数据都是存储在Spark Executor的内存中的,然后Spark Streaming启动的job会去处理那些数据。SparkStreaming 提供了一个叫DStream的高级抽象,DStream是由一个RDD序列组成,包含了一段时间间隔内的数据项的集合。

kafka数据量过大数据库插入 kafka数据接入_spark_03


kafka数据量过大数据库插入 kafka数据接入_kafka数据量过大数据库插入_04


模式二:与基于Receiver接收数据不一样,这种方式定期地从Kafka的topic+partition中查询最新的偏移量,再根据定义的偏移量范围在每个batch里面处理数据。当作业需要处理的数据来临时,spark通过调用Kafka的简单消费者API读取一定范围的数据。这种模式可以有效提高系统的吞吐量。

SparkStreaming 接收到数据流后,可使用spark进行数据业务逻辑的处理,处理之后的数据再实时的存入到数据库。

代码示例:

1、模拟流式数据:

启动kafka:
bin/kafka-server-start.sh
创建topic:
bin/kafka-topics.sh --create --partitions 3 --replication-factor 2 --topic wordCount --zookeeper node01:2181,node02:2181,node03:2181
模拟生产者:
bin/kafka-console-producer.sh --broker-list node01:9092,node02:9092,node03:9092 --topic wordCount

2、spark实时读取kafka
模式一:

package com.gw.sparkStreaming
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}
import scala.collection.immutable

/**
  * 需求:sparkstreaming来获取kafka中的数据,第一种方式:Receiver 的方式,然后进行单词计数
  *
  * 注意事项;如果要保证数据不丢失,就需要开启WAL预写日志
  *   1、.set("spark.streaming.receiver.writeAheadLog.enable", "true")
  *   2、因为数据要进行存储,必须要设置chkpoint目录
  */
object DataFromKafka1 {
  def main(args: Array[String]): Unit = {
    //初始化
    val sparkConf: SparkConf = new SparkConf()
      .setMaster("local[4]")
      .setAppName("DataFromKafka1")
      .set("spark.streaming.receiver.writeAheadLog.enable","true")
    val sc: SparkContext = new SparkContext(sparkConf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))

    //设置检查点
    ssc.checkpoint("./kafka-chk")

    //zk集群信息
    val zkQuorum="node01:2181,node02:2181,node03:2181"
    //kafka组
    val groupid="group1"
    //topic
    val topic=Map("wordCount "->2)

    //读取kafka中的数据
    //val data: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc,zkQuorum,groupid,topic)

    //这个时候相当于同时开启3个receiver接受数据
    val receiverDstream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupid, topic)
      stream
    })
    //使用ssc.union方法合并所有的receiver中的数据
    val data: DStream[(String, String)] = ssc.union(receiverDstream)

    //获取真正的数据,数据在ReceiverInputDStream[(String, String)]中元组的第二位
    val realData: DStream[String] = data.map(x=>x._2)

    //单词计数
    val result: DStream[(String, Int)] = realData.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

    //打印
    result.print()

    //开启流式计算
    ssc.start()
    ssc.awaitTermination()
  }
}

模式二:

package com.gw.sparkStreaming

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

/**
  * 需求:Kafka的topic+partition中查询最新的偏移量方式获取数据,并进行单词计数
  *
  */
object DataFromKafka2 {
  def main(args: Array[String]): Unit = {
    //初始化
    val sparkConf: SparkConf = new SparkConf()
      .setAppName("DataFromKafka2")
      .setMaster("local[4]")
      .set("spark.streaming.receiver.writeAheadLog.enable","true")
    val sc: SparkContext = new SparkContext(sparkConf)
    sc.setLogLevel("WARN")
    val ssc: StreamingContext = new StreamingContext(sc,Seconds(5))

    //设置检查点,通常生产环境当中,为了保证数据不丢失,将数据放到hdfs之上,hdfs的高容错,多副本特征
    ssc.checkpoint("./kafka-chk2")

    //设置kafkaParams
    val kafkaParams=Map("metadata.broker.list"->"node01:9092,node02:9092,node03:9092","group.id"->"group1")

    //设置topics
    val topics=Set("wordCount ")

    //获取数据
    val data: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParams,topics)

    //获取真正的数据,数据在元组的第二位
val realData: DStream[String] = data.map(x=>x._2)

//单词计数
    val result: DStream[(String, Int)] = realData.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)

    //打印
    result.print()

    //开启流式计算
    ssc.start()
    ssc.awaitTermination()
  }
}

3、结果展示:(单词+次数)

-------------------------------------------
Time: 1536325032000 ms
-------------------------------------------
(love,1)
(Beijing,1)
(I,1)
 
-------------------------------------------
Time: 1536325035000 ms
-------------------------------------------
(love,2)
(Beijing,1)
(I,2)
(Shanghai,1)
 
-------------------------------------------
Time: 1536325038000 ms
-------------------------------------------
(love,4)
(Beijing,1)
(I,2)
(Shanghai,2)