Apache Kafka是一個分散式的訊息釋出-訂閱系統。可以說,任何實時大資料處理工具缺少與Kafka整合都是不完整的。本文將介紹如何使用Spark Streaming從Kafka中接收資料,這裡將會介紹兩種方法:(1)、使用Receivers和Kafka高層次的API;(2)、使用Direct
API,這是使用低層次的KafkaAPI,並沒有使用到Receivers,是Spark 1.3.0中開始引入的。這兩種方法有不同的程式設計模型,效能特點和語義擔保。下文將會一一介紹。
基於Receivers的方法
這個方法使用了Receivers來接收資料。Receivers的實現使用到Kafka高層次的消費者API。對於所有的Receivers,接收到的資料將會儲存在Spark executors中,然後由Spark Streaming啟動的Job來處理這些資料。
然而,在預設的配置下,這種方法在失敗的情況下會丟失資料,為了保證零資料丟失,你可以在Spark Streaming中使用WAL日誌,這是在Spark 1.2.0才引入的功能,這使得我們可以將接收到的資料儲存到WAL中(WAL日誌可以儲存在HDFS上),所以在失敗的時候,我們可以從WAL中恢復,而不至於丟失資料。
下面,我將介紹如何使用這種方法來接收資料。
1、引入依賴。
對於Scala和Java專案,你可以在你的pom.xml檔案引入以下依賴:
2 |
<groupId>org.apache.spark</groupId> |
3 |
<artifactId>spark-streaming-kafka_2. 10 </artifactId> |
4 |
<version> 1.3 . 0 </version> |
如果你是使用SBT,可以這麼引入:
1 |
libraryDependencies
+= "org.apache.spark" % "spark-streaming-kafka_2.10" % "1.3.0" |
2、程式設計
在Streaming程式中,引入KafkaUtils,並建立一個輸入DStream:
1 |
import org.apache.spark.streaming.kafka. _ |
3 |
val kafkaStream = KafkaUtils.createStream(streamingContext, |
4 |
[ZK
quorum], [consumer group id], [per-topic number of Kafka partitions to consume]) |
在建立DStream的時候,你也可以指定資料的Key和Value型別,並指定相應的解碼類。
需要注意的是:
1、Kafka中Topic的分割槽和Spark Streaming生成的RDD中分割槽不是一個概念。所以,在
KafkaUtils.createStream()
增加特定主題分割槽數僅僅是增加一個receiver中消費Topic的執行緒數。並不增加Spark並行處理資料的數量;
2、對於不同的Group和tpoic我們可以使用多個receivers建立不同的DStreams來並行接收資料;
3、如果你啟用了WAL,這些接收到的資料將會被持久化到日誌中,因此,我們需要將storage level 設定為StorageLevel.MEMORY_AND_DISK_SER
,也就是:
1 |
KafkaUtils.createStream(...,
StorageLevel.MEMORY _ AND _ DISK _ SER) |
3、部署
對應任何的Spark 應用,我們都是用spark-submit
來啟動你的應用程式,對於Scala和Java使用者,如果你使用的是SBT或者是Maven,你可以將spark-streaming-kafka_2.10及其依賴打包進應用程式的Jar檔案中,並確保spark-core_2.10和
spark-streaming_2.10標記為provided,因為它們在Spark 安裝包中已經存在:
02 |
<groupId>org.apache.spark</groupId> |
03 |
<artifactId>spark-streaming _ 2.10 </artifactId> |
04 |
<version> 1.3 . 0 </version> |
05 |
<scope>provided</scope> |
09 |
<groupId>org.apache.spark</groupId> |
10 |
<artifactId>spark-core _ 2.10 </artifactId> |
11 |
<version> 1.3 . 0 </version> |
12 |
<scope>provided</scope> |
然後使用spark-submit來啟動你的應用程式。
當然,你也可以不在應用程式Jar檔案中打包spark-streaming-kafka_2.10及其依賴,我們可以在spark-submit後面加上--jars引數也可以執行你的程式:
1 |
[iteblog@
spark]$ spark-1.3.0-bin-2.6.0/bin/spark-submit --master yarn-cluster |
2 |
--class
iteblog.KafkaTest |
3 |
--jars
lib/spark-streaming-kafka_2.10-1.3.0.jar, |
4 |
lib/spark-streaming_2.10-1.3.0.jar, |
5 |
lib/kafka_2.10-0.8.1.1.jar,lib/zkclient-0.3.jar, |
6 |
lib/metrics-core-2.2.0.jar
./iteblog-1.0-SNAPSHOT.jar |
下面是一個完整的例子:
01 |
object KafkaWordCount
{ |
02 |
def main(args : Array[String])
{ |
03 |
if (args.length
< 4 )
{ |
04 |
System.err.println( "Usage:
KafkaWordCount <zkQuorum> <group> <topics> <numThreads>" ) |
08 |
StreamingExamples.setStreamingLogLevels() |
10 |
val Array(zkQuorum,
group, topics, numThreads) = args |
11 |
val sparkConf = new SparkConf().setAppName( "KafkaWordCount" ) |
12 |
val ssc = new StreamingContext(sparkConf,
Seconds( 2 )) |
13 |
ssc.checkpoint( "checkpoint" ) |
15 |
val topicMap = topics.split( "," ).map(( _ ,numThreads.toInt)).toMap |
16 |
val lines = KafkaUtils.createStream(ssc,
zkQuorum, group, topicMap).map( _ . _ 2 ) |
17 |
val words = lines.flatMap( _ .split( "
" )) |
18 |
val wordCounts = words.map(x = >
(x, 1 L)) |
19 |
.reduceByKeyAndWindow( _ + _ , _ - _ ,
Minutes( 10 ),
Seconds( 2 ), 2 ) |
23 |
ssc.awaitTermination() |
尊重原創,轉載請註明: 轉載自過往記憶(http://www.iteblog.com/)
本文連結地址: 《Spark Streaming和Kafka整合開發指南(一)》(http://www.iteblog.com/archives/1322)