组件介绍:
Apache Kafka 是一个可扩展,高性能,低延迟的消息队列,允许我们像消息系统一样读取和写入数据。
Spark Streaming 是 Apache Spark 的一部分,是一个可扩展、高吞吐、容错的实时流处理引擎。使用 Scala 语言开发的,支持 Java API。
技术架构:
Kafka有两类客户端,Producer(消息生产者的)和Consumer(消息消费者)。消息生产者将应用程序的流式数据发送到Kafka,Kafka集群中每个节点都有一个被称为broker的实例,负责缓存数据。Kafka中不同业务系统的消息可通过topic进行区分,每个消息都会被分区,用以分担消息读写负载,每个分区又可以有多个副本来防止数据丢失。消费者在具体消费某个topic消息时,指定起始偏移量。
SparkStreaming读取Kafka数据源有两种模式:
模式一:SparkStreaming 基于Receiver接收Kafka数据流,Receiver是使用Kafka的高层次Consumer API来实现的。receiver从Kafka中获取的数据都是存储在Spark Executor的内存中的,然后Spark Streaming启动的job会去处理那些数据。SparkStreaming 提供了一个叫DStream的高级抽象,DStream是由一个RDD序列组成,包含了一段时间间隔内的数据项的集合。
模式二:与基于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)