工具篇-Spark-Streaming获取kafka数据的两种方式(转载)

转载自:https://blog.csdn.net/weixin_41615494/article/details/7952173

一、基于Receiver的方式

原理

Receiver从Kafka中获取的数据存储在Spark Executor的内存中,然后Spark Streaming启动的job会去处理那些数据,如果突然数据暴增,大量batch堆积,很容易出现内存溢出的问题。 
在默认的配置下,这种方式可能会因为底层失败而丢失数据。如果要让数据零丢失,就必须启用Spark Streaming的预写日志机制(Write Ahead Log,WAL),该机制会同步地将接收到的Kafka数据写入分布式文件系统(比如HDFS)上的预写日志中,即使底层节点出现了失败,也可以使用预写日志中的数据进行恢复。

要点

1. Kafka中Topic的Partition与Spark中RDD的Partition没有关系。所以,在KafkaUtils.createStream()中,提高partition的数量只会增加一个Receiver中读取Partition线程的数量,不会增加Spark处理数据的并行度。可以创建多个Kafka输入DStream,使用不同的Consumer Group和Topic,来通过多个Receiver并行接收数据。

2. 如果基于容错的文件系统,比如HDFS,启用了预写日志机制,接收到的数据都会被复制一份到预写日志中。此时在KafkaUtils.createStream()中,设置的持久化级别是StorageLevel.MEMORY_AND_DISK_SER。

这部分代码虽然整理了但没测试,代码如下:

 1 import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream}
 2 import org.apache.spark.streaming.kafka.KafkaUtils
 3 import org.apache.spark.streaming.{Seconds, StreamingContext}
 4 import org.apache.spark.{SparkConf, SparkContext}
 5 
 6 import scala.collection.immutable
 7 
 8 
 9 object KafkaReceiver {
10 
11   def main(args: Array[String]): Unit = {
12         //1、创建sparkConf
13         val sparkConf = new SparkConf().setMaster("local[2]").setAppName("SparkStreamingKafka_Receiver").set("spark.streaming.receiver.writeAheadLog.enable","true")
14         // 开启wal预写日志,保存数据源的可靠性
15 
16         val sc = new SparkContext(sparkConf)
17         sc.setLogLevel("WARN")
18         //3、创建StreamingContext
19         val ssc = new StreamingContext(sc, Seconds(5))
20 
21         //设置checkpoint
22         ssc.checkpoint("./Kafka_Receiver_test")
23         //4、定义zk地址
24         val zkQuorum="127.0.0.1:2181"
25         //5、定义消费者组
26         val groupId="spark_receiver_test"
27         //6、定义topic相关信息 Map[String, Int]
28         // 这里的value并不是topic分区数,它表示的topic中每一个分区被N个线程消费
29         val topics=Map("druid-monitor-data" -> 2)
30 
31         //7、通过KafkaUtils.createStream对接kafka
32         //这个时候相当于同时开启3个receiver接受数据
33         val receiverDstream: immutable.IndexedSeq[ReceiverInputDStream[(String, String)]] = (1 to 3).map(
34               x => {
35                     val stream: ReceiverInputDStream[(String, String)] = KafkaUtils.createStream(ssc, zkQuorum, groupId, topics)
36                     stream
37               }
38         )
39         //使用ssc.union方法合并所有的receiver中的数据
40         val unionDStream: DStream[(String, String)] = ssc.union(receiverDstream)
41         //8、获取topic中的数据
42         val topicData: DStream[String] = unionDStream.map(_._2)
43         //9、切分每一行,每个单词计为1
44         val wordAndOne: DStream[String] = topicData.flatMap(_.split(" "))
45         //10、打印输出
46         wordAndOne.print()
47 
48         //开启计算
49         ssc.start()
50         ssc.awaitTermination()
51   }
52 }

pom文件部分内容为:

 1     <properties>
 2         <spark.version>2.4.2</spark.version>
 3         <scala.version>2.11</scala.version>
 4     </properties>
 5 
 6 
 7     <!--<repositories>
 8         <repository>
 9             <id>SparkPackagesRepo</id>
10             <url>http://dl.bintray.com/spark-packages/maven</url>
11         </repository>
12     </repositories>-->
13 
14 
15     <dependencies>
16         <dependency>
17             <groupId>org.apache.spark</groupId>
18             <artifactId>spark-core_${scala.version}</artifactId>
19             <version>${spark.version}</version>
20         </dependency>
21         <dependency>
22             <groupId>org.apache.spark</groupId>
23             <artifactId>spark-streaming_${scala.version}</artifactId>
24             <version>${spark.version}</version>
25         </dependency>
26         <dependency>
27             <groupId>org.apache.spark</groupId>
28             <artifactId>spark-sql_${scala.version}</artifactId>
29             <version>${spark.version}</version>
30         </dependency>
31         <dependency>
32             <groupId>org.apache.spark</groupId>
33             <artifactId>spark-hive_${scala.version}</artifactId>
34             <version>${spark.version}</version>
35         </dependency>
36         <dependency>
37             <groupId>org.apache.spark</groupId>
38             <artifactId>spark-mllib_${scala.version}</artifactId>
39             <version>${spark.version}</version>
40         </dependency>
41         <!--<dependency>
42             <groupId>org.apache.spark</groupId>
43             <artifactId>spark-streaming-kafka-0-10_${scala.version}</artifactId>
44             <version>${spark.version}</version>
45         </dependency>-->
46         <dependency>
47             <groupId>org.apache.spark</groupId>
48             <artifactId>spark-streaming-kafka-0-8_${scala.version}</artifactId>
49             <version>${spark.version}</version>
50         </dependency>
51         <dependency>
52             <groupId>com.alibaba</groupId>
53             <artifactId>fastjson</artifactId>
54             <version>1.2.47</version>
55         </dependency>
56 
57     </dependencies>
58 
59     <build>
60         <plugins>
61 
62             <plugin>
63                 <groupId>org.scala-tools</groupId>
64                 <artifactId>maven-scala-plugin</artifactId>
65                 <version>2.15.2</version>
66                 <executions>
67                     <execution>
68                         <goals>
69                             <goal>compile</goal>
70                             <goal>testCompile</goal>
71                         </goals>
72                     </execution>
73                 </executions>
74             </plugin>
75 
76             <plugin>
77                 <artifactId>maven-compiler-plugin</artifactId>
78                 <version>3.6.0</version>
79                 <configuration>
80                     <source>1.8</source>
81                     <target>1.8</target>
82                 </configuration>
83             </plugin>
84 
85             <plugin>
86                 <groupId>org.apache.maven.plugins</groupId>
87                 <artifactId>maven-surefire-plugin</artifactId>
88                 <version>2.19</version>
89                 <configuration>
90                     <skip>true</skip>
91                 </configuration>
92             </plugin>
93         </plugins>
94     </build>

 

二、基于Direct的方式

原理

该方式在Spark 1.3中引入,来确保更加健壮的机制。这种方式会周期性地查询Kafka,获取每个Topic+Partition的最新的Offset,从而定义每个batch的offset的范围。当处理数据的job启动时,就会使用Kafka的简单consumer api来获取Kafka指定offset范围的数据。 

优点

1. 简化并行读取:在Kafka Partition和RDD Partition之间,有一个一对一的映射关系,所以如果要读取多个partition,不需要创建多个输入DStream然后对它们进行Union操作,Spark会创建跟Kafka Partition一样多的RDD Partition,并且会并行从Kafka中读取数据。 
2. 高性能:如果要保证零数据丢失,在基于Receiver的方式中,需要开启WAL机制,这种方式数据实际上被复制了两份效率低下,Kafka自己本身就有高可靠的机制,会对数据复制一份。而基于Direct的方式,不依赖Receiver,不需要开启WAL机制,只要Kafka中作了数据的复制,就可以通过Kafka的副本进行恢复。 

对比(在实际生产环境中大都用Direct方式):

基于Receiver的方式,使用Kafka的高阶API在ZooKeeper中保存消费过的offset的,这是消费Kafka数据的传统方式。这种方式配合WAL机制可以保证数据零丢失的高可靠性,但是却无法保证数据被处理一次且仅一次,可能会重复处理。因为Spark和ZooKeeper之间可能是不同步的。

基于Direct的方式,使用Kafka的简单API,Spark Streaming自己就负责追踪消费的Offset,并保存在Checkpoint中,Spark自己一定是同步的,因此可以保证数据是消费一次且仅消费一次。

 

posted @ 2019-04-28 23:03  akia开凯  阅读(601)  评论(0编辑  收藏  举报