SparkStreaming14(Kafka数据源与spark对接)

文章目录

  • Apache Kafka数据源0.8版本对接方式
    • 三台机器安装kafka集群
    • 第一种方式对接kafka之CreateDstream方式
    • 第二种方式对接kafka之CreateDirectStream方式
  • Apche kafka数据源0.10版本对接

Apache Kafka数据源0.8版本对接方式

kafka作为一个实时的分布式消息队列,实时的生产和消费消息,这里我们可以利用SparkStreaming实时地读取kafka中的数据,然后进行相关计算。
在Spark1.3版本后,KafkaUtils里面提供了两个创建dstream的方法,一种为KafkaUtils.createDstream,另一种为KafkaUtils.createDirectStream。
官方文档说明
http://spark.apache.org/docs/2.2.0/streaming-kafka-integration.html

三台机器安装kafka集群

1 下载kafka安装压缩包
http://archive.apache.org/dist/kafka/

2 上传压缩包并解压
将kafka的安装包上传到第一台服务器的/export/softwares路径下面去,然后解压到/export/servers
这里统一使用 kafka_2.11-1.0.0.tgz 这个版本

3 修改kafka配置文件
第一台机器修改kafka配置文件server.properties

broker.id=0
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/export/servers/kafka_2.11-1.0.0/logs
num.partitions=2
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.flush.interval.messages=10000
log.flush.interval.ms=1000
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=node01:2181,node02:2181,node03:2181
zookeeper.connection.timeout.ms=6000
group.initial.rebalance.delay.ms=0
delete.topic.enable=true
host.name=node01

第二台机器修改kafka配置文件server.properties

broker.id=1
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/export/servers/kafka_2.11-1.0.0/logs
num.partitions=2
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.flush.interval.messages=10000
log.flush.interval.ms=1000
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=node01:2181,node02:2181,node03:2181
zookeeper.connection.timeout.ms=6000
group.initial.rebalance.delay.ms=0
delete.topic.enable=true
host.name=node02

第三台机器修改kafka配置文件server.properties

broker.id=2
num.network.threads=3
num.io.threads=8
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/export/servers/kafka_2.11-1.0.0/logs
num.partitions=2
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.flush.interval.messages=10000
log.flush.interval.ms=1000
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=node01:2181,node02:2181,node03:2181
zookeeper.connection.timeout.ms=6000
group.initial.rebalance.delay.ms=0
delete.topic.enable=true
host.name=node03

4启动kafka集群
三台机器启动kafka服务

bin/kafka-server-start.sh config/server.properties > /dev/null 2>&1 &    

第一种方式对接kafka之CreateDstream方式

KafkaUtils.createDstream(ssc, [zk], [group id], [per-topic,partitions] ) 使用了receivers接收器来接收数据,利用的是Kafka高层次的消费者api,对于所有的receivers接收到的数据将会保存在Spark executors中,然后通过Spark Streaming启动job来处理这些数据,默认会丢失,可启用WAL日志,它同步将接受到数据保存到分布式文件系统上比如HDFS。 所以数据在出错的情况下可以恢复出来 。

SparkStreaming14(Kafka数据源与spark对接)_第1张图片
A、创建一个receiver接收器来对kafka进行定时拉取数据,这里产生的dstream中rdd分区和kafka的topic分区不是一个概念,故如果增加特定topic的分区数仅仅是增加一个receiver中消费topic的线程数,并没有增加spark的并行处理的数据量。
B、对于不同的group和topic可以使用多个receivers创建不同的DStream
C、如果启用了WAL(spark.streaming.receiver.writeAheadLog.enable=true)
同时需要设置存储级别(默认StorageLevel.MEMORY_AND_DISK_SER_2),
第一步:导入jar包

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
    <version>2.2.0</version>
</dependency>

第二步:创建kafka的topic
node01服务器执行以下命令创建kafka的topic sparkafka

cd /export/servers/kafka_2.11-1.0.0/
bin/kafka-topics.sh  --create --partitions 3 --replication-factor 2 --topic sparkafka --zookeeper node01:2181,node02:2181,node03:2181

第三步:使用脚本启动kafka生产者
node01服务器执行以下命令通过脚本模拟kafka生产者

cd /export/servers/kafka_2.11-1.0.0/
bin/kafka-console-producer.sh --broker-list node01:9092,node02:9092,node03:9092 --topic sparkafka

第四步:开发SparkStreaming对接kafka代码

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
import org.apache.spark.streaming.kafka.KafkaUtils
import scala.collection.immutable

object StreamKafkaReceiver {
  def main(args: Array[String]): Unit = {
    //1、创建sparkConf
    val sparkConf: SparkConf = new SparkConf()
      .setAppName("SparkStreamingKafka_Receiver")
      .setMaster("local[4]")
      .set("spark.streaming.receiver.writeAheadLog.enable", "true") //开启wal预写日志,保存数据源的可靠性
    //2、创建sparkContext
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("WARN")
    //3、创建StreamingContext
    val ssc = new StreamingContext(sc, Seconds(5))
    //设置checkpoint
    ssc.checkpoint("./Kafka_Receiver")
    //4、定义zk地址
    val zkQuorum = "node01:2181,node02:2181,node03:2181"
    //5、定义消费者组
    val groupId = "sparkafka_group"
    //6、定义topic相关信息 Map[String, Int]
    // 指定消费的topic的名称和消费topic的线程数
    val topics = Map("sparkafka" -> 3)
    //7、通过KafkaUtils.createDStream对接kafka
    //这个时候相当于同时开启3个receiver接受数据
    val receiverDstream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(x => {
      val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
      stream
    }
    )
    //使用ssc.union方法合并所有的receiver中的数据
    val unionDStream: DStream[(String, String)] = ssc.union(receiverDstream)

    //8、获取topic中的数据
    val topicData: DStream[String] = unionDStream.map(_._2)
    //9、切分每一行,每个单词计为1
    val wordAndOne: DStream[(String, Int)] = topicData.flatMap(_.split(" ")).map((_, 1))
    //10、相同单词出现的次数累加
    val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_ + _)
    //11、打印输出
    result.print()
    //开启计算
    ssc.start()
    ssc.awaitTermination()
  }
}

第二种方式对接kafka之CreateDirectStream方式

使用kafka的低阶API进行消费,消费数据的offset全部维护在kafka当中的一个topic当中,会自动提交offset,同时也可以手动提交维护offset更加安全

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

//todo:利用sparkStreaming对接kafka实现单词计数----采用Direct(低级API)
object SparkStreamingKafka_Direct {
  def main(args: Array[String]): Unit = {
    //1、创建sparkConf
    val sparkConf: SparkConf = new SparkConf()
      .setAppName("SparkStreamingKafka_Direct")
      .setMaster("local[2]")
    //2、创建sparkContext
    val sc = new SparkContext(sparkConf)
    sc.setLogLevel("WARN")
    //3、创建StreamingContext
    val ssc = new StreamingContext(sc, Seconds(5))
    ssc.checkpoint("./Kafka_Direct")
    //4、配置kafka相关参数
    val kafkaParams = Map("metadata.broker.list" -> "node01:9092,node02:9092,node03:9092", "group.id" -> "Kafka_Direct")
    //5、定义topic
    val topics = Set("sparkafka")
    //6、通过 KafkaUtils.createDirectStream接受kafka数据,这里采用是kafka低级api偏移量不受zk管理
    val dstream: InputDStream[(String, String)] = KafkaUtils.createDirectStream[String, String, StringDecoder, StringDecoder](ssc, kafkaParams, topics)
    //7、获取kafka中topic中的数据
    val topicData: DStream[String] = dstream.map(_._2)
    //8、切分每一行,每个单词计为1
    val wordAndOne: DStream[(String, Int)] = topicData.flatMap(_.split(" ")).map((_, 1))
    //9、相同单词出现的次数累加
    val result: DStream[(String, Int)] = wordAndOne.reduceByKey(_ + _)
    //10、打印输出
    result.print()
    //开启计算
    ssc.start()
    ssc.awaitTermination()
  }
}

Apche kafka数据源0.10版本对接

手动提交offset,进行offset的管理维护,保证数据不会丢失,推荐使用
第一步:导入jar包

<!-- <dependency>
	<groupId>org.apache.spark</groupId>
	<artifactId>spark-streaming-kafka-0-8_2.11</artifactId>
	<version>2.2.0</version>
</dependency>-->
<dependency>
	<groupId>org.apache.spark</groupId>
	<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
	<version>2.2.0</version>
</dependency>

第二步:整合代码开发
手动提交offset管理,保证数据消费exactly once

import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.streaming.kafka010._
import org.apache.spark.{SparkConf, SparkContext, TaskContext}
import org.apache.spark.streaming.{Seconds, StreamingContext}
object StreamingKafka1 {
  def main(args: Array[String]): Unit = {
    val conf = new SparkConf().setMaster("local[4]").setAppName("NetworkWordCount")
    val context = new SparkContext(conf)
    context.setLogLevel("WARN")
    val ssc = new StreamingContext(context, Seconds(1))
    //创建topic
    val brokers= "node01:9092,node02:9092,node03:9092"
    val sourcetopic="sparkafka";
    //创建消费者组
    var group="sparkafkaGroup"
    //消费者配置
    val kafkaParam = Map(
      "bootstrap.servers" -> brokers,//用于初始化链接到集群的地址
      "key.deserializer" -> classOf[StringDeserializer],
      "value.deserializer" -> classOf[StringDeserializer],
      //用于标识这个消费者属于哪个消费团体
      "group.id" -> group,
      //如果没有初始化偏移量或者当前的偏移量不存在任何服务器上,可以使用这个配置属性
      //可以使用这个配置,latest自动重置偏移量为最新的偏移量
      "auto.offset.reset" -> "latest",
      //如果是true,则这个消费者的偏移量会在后台自动提交
      "enable.auto.commit" -> (false: java.lang.Boolean)
    );
    var stream = KafkaUtils.createDirectStream[String,String](ssc,LocationStrategies.PreferConsistent,ConsumerStrategies.Subscribe[String,String](Array("sparkafka"),kafkaParam))
    //循环遍历每个RDD当中的数据
    stream.foreachRDD(f =>{
      //判断如果rdd当中有数据,就进行处理,没有数据就不用处理
      if(f.count() > 0){
        println("接收kafka当中的数据")
        //每个分区当中的数据进行循环遍历,遍历每个分区当中每一行的数据
        f.foreach(f =>{
          //获取kafka当中的数据内容
          val kafkaValue: String = f.value()
          println(kafkaValue)
        })
        //打印offset的信息
        val offsetRanges: Array[OffsetRange] = f.asInstanceOf[HasOffsetRanges].offsetRanges

        f.foreachPartition { iter =>
          val o: OffsetRange = offsetRanges(TaskContext.get.partitionId)
          println(s"${o.topic} ${o.partition} ${o.fromOffset} ${o.untilOffset}")
        }
        println("=============================")
        // 等输出操作完成后提交offset
        stream.asInstanceOf[CanCommitOffsets].commitAsync(offsetRanges)
      }
    })
    ssc.start()
    ssc.awaitTermination()
  }
}

你可能感兴趣的:(spark,大数据,kafka,大数据,spark)