低级api消费:KafkaUtils.createDirectStream方式
这种方式不同于Receiver(高级api)接收数据,它定期地从kafka的topic下对应的partition中查询最新的偏移量,再根据偏移量范围在每个batch里面处理数据,Spark通过调用kafka简单的消费者Api(低级api)读取一定范围的数据。
相比基于Receiver方式有几个优点:
A、简化并行
不需要创建多个kafka输入流,然后union它们,sparkStreaming将会创建和kafka分区数相同的rdd的分区数,而且会从kafka中并行读取数据,spark中RDD的分区数和kafka中的topic分区数是一一对应的关系。
B、高效
第一种实现数据的零丢失是将数据预先保存在WAL中,会复制一遍数据,会导致数据被拷贝两次,第一次是接受kafka中topic的数据,另一次是写到WAL中。而没有receiver的这种方式消除了这个问题。
C、恰好一次语义(Exactly-once-semantics)
Receiver读取kafka数据是通过kafka高层次api把偏移量写入zookeeper中,虽然这种方法可以通过数据保存在WAL中保证数据不丢失,但是可能会因为sparkStreaming和ZK中保存的偏移量不一致而导致数据被消费了多次。EOS通过实现kafka低层次api,偏移量仅仅被ssc保存在checkpoint中,消除了zk和ssc偏移量不一致的问题。缺点是无法使用基于zookeeper的kafka监控工具。
package Kafka2SparkStreaming
import kafka.serializer.StringDecoder
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, InputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
object LowApiKafak2Spark {
/*低级api*/
def main(args: Array[String]): Unit = {
//appName设置为当前类名LowApi_CreateDirectDstream,本地模式至少要两个核
val sparkConf: SparkConf =new SparkConf().setAppName("LowApiKafak2Spark").setMaster("local[2]")
val sc: SparkContext =new SparkContext(sparkConf)
sc.setLogLevel("WARN")
//创建SparkstreamingContext
val ssc: StreamingContext =new StreamingContext(sc,Seconds(10))
ssc.checkpoint("./checkpoint")
//设置kafka相关参数
val kafkaParam: Map[String, String] =Map("metadata.broker.list"->"newnode-1:9092,newnode-2:9092,newnode-3:9092",
"group.id"->"Kafka_Direct")
//定义topic,#可以拉取多个topic的数据
val topics =Set("spark_01")
//创建DStream
val dstream: InputDStream[(String, String)] =KafkaUtils.createDirectStream[String,String,StringDecoder,StringDecoder](ssc,kafkaParam,topics)
//获取kafka中topic的数据 取每个元组的第二个
val topicData: DStream[String] =dstream.map(_._2)
//单词切分
val result: DStream[(String, Int)] =topicData.flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
//打印输出
result.print()
ssc.start()
ssc.awaitTermination()
//先启动zk
//运行时需要先启动kafka集群
// kafka-server-start.sh /export/servers/kafka/config/server.properties
//创建topic
//kafka-topics.sh --create --zookeeper 192.168.213.8:2080 --replication-factor 1 --partitions 3 --topic spark_01
//向topic中生产数据
//通过shell命令向topic发送消息
// kafka-console-producer.sh --broker-list 192.168.213.8:9092 --topic spark_01
}
在向kafka生产者控制台写了数据,然后运行spark程序,程序报错:couldn‘t find leader offset fot set。发现原因是metadata.broker.list的kafka地址不能写ip,要写主机名。在windows本地的c:/windows/system32/drivers/etc的host文件中加上ip主机名的映射即可
解释程序中的一个小语法
map(_._2)的含义如下
map(_._n)表示任意元组tuple对象,后面的数字n表示取第几个数.(n>=1的整数)
val p=List((“hello”,35,1.50),(“nihao”,36,1.78))
res9: List[(String, Int, Double)] = List((hello,35,1.5), (nihao,36,1.78))
scala> p.map(_._1)
res10: List[String] = List(hello, nihao)
scala> p.map(_._2)
res11: List[Int] = List(35, 36)
scala> p.map(_._3)
res12: List[Double] = List(1.5, 1.78)
元组取值就是t._1,其中t为元组名