我们前面SparkStreaming获取数据的来源是TCP,但是平常是不会这么用的,我们通常用的是Kafka。
SparkStreamingContext是不直接提供对Kafka的访问的。
这个时候就有KafkaUtils
这里有两个方法
1.createDirectStream,是一种直连方式,他很重要,因为他用的是Kafka的底层API,他在消费的时候,会直接连到Kafka的分区上。
2.createStream,是有接收者的方式,而刚才我们讲的createDirectStream是没有接收者的方式。这是一种简单的方式,这种简单的方式在很早之前就出现了,但是他有一个问题,就是容易丢失数据,并且它的效率也比较低。
我们这里先使用这种简单的方式,这种简单方式其实他会自动帮我们维护偏移量,如果我们想要使用又高效,又不丢失数据的方式,我们就要手动维护偏移量。
使用这种傻瓜式的方式,我们首先要传进去StreamingContext,
第二个参数是zkQuorm,这个参数是zookeeper的连接地址
第三个参数是组id,我们知道消费者有一个消费者组,而我们SparkStreaming程序提交到集群中是分布式运行的。他相当于有很多的消费者。很多的消费者要消费同一份数据。他们不能重复消费,有了消费者组之后,就不会产生交叉消费的情况。
第四个参数是topic,他是一个Map,Map的key是主题名,value是分区数量
第五个参数是存储级别
配置完之后我们就可以从Kafka中获取数据,我们可以看到他的返回值为DStream类型的键值对,key为主题名称,value为值:
完整代码如下:
package com.test.sparkStreaming
import org.apache.spark.SparkConf
import org.apache.spark.storage.StorageLevel
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import org.apache.spark.streaming.{Seconds, StreamingContext}
object KafkaStreamingWordCount {
def main(args: Array[String]): Unit = {
Logger.getLogger("org.apache.spark").setLevel(Level.OFF)
val conf: SparkConf = new SparkConf().setAppName("KafkaStreamingWordCount").setMaster("local[2]")
//创建一个SparkStreamingContext
val ssc: StreamingContext = new StreamingContext(conf,Seconds(5))
//如果想要更新历史状态(累加),要设置checkpoint
ssc.checkpoint("./ck")
//从Kafka中拉取数据
val zkQuorum = "marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,marshal04:2181,marshal05:2181"
val groupId = "g1"
val topic = Map("wordcount" -> 2)
val lines: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc,zkQuorum,groupId,topic ,StorageLevel.MEMORY_ONLY)
//获取Kafka中的每一行内容
val line: DStream[String] = lines.map(_._2)
val result: DStream[(String, Int)] = line.flatMap(_.split(" ").map((_,1))).reduceByKey(_+_)
result.print()
ssc.start()
ssc.awaitTermination()
}
}
在执行之前首先我们检查一下我们有没有创建过wordcount的topic
接下来我们创建一个topic为wordcount
bin/kafka-topics.sh --create --zookeeper marshal:2181,marshal01:2181,marshal02:2181,marshal03:2181,
marshal04:2181,marshal05:2181 --replication-factor 3 --partitions 3--topic wordcount
我们可以看到这里有个分区,那么这里的分区和之前RDD的分区和mapreduce的分区是不一样的。
Kafka的分区就意味着在一台机器上分3个地方进行存储。一个分区会在其他机器上保存几份副本。
如果是hdfs中的数据会被切分成多个block块,而kafka中的数据会被存成多个分区。
创建完主题之后我们启动一个生产者,再执行程序观察效果: