一个用Kakfa低级api的SparkStreaming程序实例

低级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为元组名


 

猜你喜欢

转载自blog.csdn.net/someInNeed/article/details/89738609
今日推荐