总体介绍:Direct方式采用Kafka简单的consumer api方式来读取数据,这种方法不再需要专门Receiver来持续不断读取数据。当batch任务触发时,由Executor读取数据,并参与其他Executor的数据计算过程中去。driver来决定读取多少offsets,并将offsets交由checkpoints来维护。将触发下次batch任务,再由Executor读取Kafka数据并计算。
Direct方式的优点:
1、简化并行读取:如果要读取多个partition,不需要创建多个输入DStream,然后对他们进行union操作,spark会创建和Kafka partition一样多的RDD partition,并且会并行从Kafka读取数据。
2、高性能:如果要保证零数据丢失,基于direct的方式,不依赖Receiver,不需要开启WAL机制,只要Kafka中做了数据的复制,就可以通过Kafka副本恢复数据。
3、一次且仅一次的事务机制:基于direct方式,使用Kafka简单的api,Spark Streaming自己就负责offset的追踪,并保存在checkpoint中。Spark自己肯定是同步的,因此可以保证数据是消费一次且仅消费一次的。
step1:pom.xml依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spark.version>2.3.4</spark.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-streaming-kafka-0-10 -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
</dependencies>
step:idea编写代码
package com.sparkstreaming
import org.apache.kafka.clients.consumer.{ConsumerConfig, ConsumerRecord}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.dstream.InputDStream
import org.apache.spark.streaming.kafka010.{ConsumerStrategies, KafkaUtils, LocationStrategies}
import org.apache.spark.streaming.{Seconds, StreamingContext}
object SparkKafkaDirectDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setMaster("local[*]").setAppName("flumeDemo")
val ssc = new StreamingContext(conf,Seconds(5))
//SparkStreaming消费Kafka数据
val kafkaParams=Map(
(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG->"192.168.56.116:9092"),
("value.deserializer"->"org.apache.kafka.common.serialization.StringDeserializer"),
("key.deserializer"->"org.apache.kafka.common.serialization.StringDeserializer"),
(ConsumerConfig.GROUP_ID_CONFIG->"kafkaGroup01")
)
//消费kafka中wordcount消息队列
val message: InputDStream[ConsumerRecord[String, String]] = KafkaUtils.createDirectStream(ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe(Set("wordcount"), kafkaParams)
)
message.map(x=>x.value()).flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_)
.print()
ssc.start()
ssc.awaitTermination()
}
}
KafkaUtils.createDirectStream()需要三个参数,介绍一下后两个:
- LocationStrategies 表示 Consumer 调度分区的位置策略。
➢ LocationStrategies.PreferConsistent : 每 个 Spark Executor 作 为 一 个Consumer,Executor 与 Kafka Partition 一对一 。大多数情况下使用,主要是为了均匀分布。
➢ LocationStrategies.PreferBrokers:每个 Spark Executor 与 Kafka Broker 在相同 HOST 上。也就是说 Spark Executor 优先分配到 Kafka Broker 上。
➢ LocationStrategies.PreferFixed:Spark Executor 与 Kafka Partition 使用固定的映射(fixed mapping)。如果负载不均衡,可以通过这种方式来手动指定 分 配 方 式 , 当 其 他 没 有 在 fixed mapping 中 指 定 的 , 均 采 用PreferConsistent 方式分配。函数原型如下所示,其中 hostMap 就是TopicPartition 和主机地址的映射。 - ConsumerStrategies 指消费策略。
➢ ConsumerStrategies.Subscribe:允许订阅固定的主题集合。
➢ ConsumerStrategies.Assign:指定固定的分区集合。
step3:创建topic wordcount
kafka-topics.sh --create --zookeeper 192.168.***.116:2181 --topic wordcount --replication-factor 1 --partitions 1
step4:在idea中启动
step5:利用producer实时的向wordcount中生产数据,此时idea控制台就会对输入的消息进行实时统计
kafka-console-producer.sh --topic wordcount --broker-list 192.168.***.116:9092