Spark Streaming整合kafka

本來Streaming整合kafka是由兩種方式的,第一種是Receiver DStream,第二種是Direct DStream

但是由于目前kafka版本升級到2.0以上了,并且我用的kafka版本是/usr/local/kafka_2.11-2.1.1,我們就不再介紹Receiver DStream這種方式了。

為什么要使用Direct DStream方式尼?他又三個大的優點,如下:

1、簡化的并行性:使用DirectStream,Spark流將創建與要使用的Kafka分區一樣多的RDD分區,這些分區都將并行地從Kafka讀取數據。所以在Kafka和RDD分區之間有一對一的映射,這更容易理解和調整

簡化的并行性

2、高效率:在第一種方法中實現零數據丟失需要將數據存儲在提前寫入日志中,從而進一步復制數據。這實際上是低效的,因為數據有效地被復制了兩次—一次是由Kafka復制的,第二次是由提前寫入日志復制的。第二種方法消除了問題,因為沒有接收器,因此不需要提前寫入日志。只要您有足夠的卡夫卡保留,就可以從卡夫卡恢復消息。

高效性

3、恰好一次語義:第一種方法使用Kafka的高級API將消耗的偏移存儲在ZooKeeper中。這是傳統上使用卡夫卡數據的方法。雖然這種方法(與提前寫入日志結合)可以確保零數據丟失(即至少一次語義),但在某些故障下,某些記錄可能會被消耗兩次的可能性很小。這是因為火花流可靠接收的數據與ZooKeeper跟蹤的偏移量不一致。因此,在第二種方法中,我們使用不使用ZooKeeper的簡單Kafka API。偏移量由檢查點內的火花流跟蹤。這消除了Spark流和ZooKeeper/Kafka之間的不一致性,因此盡管失敗,Spark流有效地接收到每個記錄一次。為了實現結果輸出的一次性語義,將數據保存到外部數據存儲的輸出操作必須是等冪的,或者是保存結果和偏移量的原子事務

但是這種方式有一個缺點:這種方式不能在zk中更新偏移量,因此也不能通過zk的監控工具看到運行進度,因此需要你自己處理這個偏移量,

下面是我寫的整合步驟樣例:、

第一步、啟動zk和kafka、創建生產者和消費者

啟動zk?

./zkServer.sh start

啟動kafka

./kafka-server-start.sh ${KAFKA_HOME}/config/server.properties

創建topic

./kafka-topics.sh --create --zookeeper 10.101.3.3:2181 --replication-factor 1 --partitions 1 --topic kafka_streaming

查看是否創建成功

./kafka-topics.sh --list --zookeeper 10.101.3.3:2181

創建topic的對應的生產者

./kafka-console-producer.sh --broker-list 10.101.3.3:9092 --topic kafka_streaming

創建topic對應的消費者

./kafka-console-consumer.sh --bootstrap-server 10.101.3.3:9092 --topic kafka_streaming --from-beginning

檢驗下是否能夠正常通訊:

第二步、添加spark-streaming-kafka依賴jar包

第三步、使用jdk1.8和kafkka0.10_2.11和spark streaming2.11版本來編寫整合程序。

首先我們先解釋下用到的兩個特殊類。

LocationStrategies

新的Kafka使用者API將預先獲取消息到緩沖區。因此,出于性能原因,Spark集成將緩存的消費者保留在執行程序上(而不是為每個批處理重新創建它們),并且更喜歡在具有適當使用者的主機位置上安排分區,這一點很重要。

在大多數情況下,您應該使用LocationStrategies.PreferConsistent,如上所示。這將在可用執行程序之間均勻分配分區。如果您的執行程序與Kafka代理在同一主機上,請使用PreferBrokers,它更愿意為該分區安排Kafka領導者的分區。最后,如果分區之間的負載有明顯偏差,請使用PreferFixed。這允許您指定分區到主機的顯式映射(任何未指定的分區將使用一致的位置)。

消費者的緩存的默認最大大小為64.如果您希望處理超過(64 *個執行程序數)Kafka分區,則可以通過spark.streaming.kafka.consumer.cache.maxCapacity更改此設置。

如果要禁用Kafka使用者的緩存,可以將spark.streaming.kafka.consumer.cache.enabled設置為false。可能需要禁用緩存來解決SPARK-19185中描述的問題。一旦SPARK-19185解決,可以在Spark的更高版本中刪除此屬性。

緩存由topicpartition和group.id鍵入,因此每次調用createDirectStream時都要使用單獨的group.id。?

ConsumerStrategies

新的Kafka消費者API有許多不同的方法來指定主題,其中一些需要相當大的后對象實例化設置。 ConsumerStrategies提供了一種抽象,即使從檢查點重新啟動后,Spark也可以獲得正確配置的消費者。

ConsumerStrategies.Subscribe,如上所示,允許您訂閱固定的主題集合。 SubscribePattern允許您使用正則表達式來指定感興趣的主題。請注意,與0.8集成不同,使用Subscribe或SubscribePattern應響應在正在運行的流期間添加分區。最后,Assign允許您指定固定的分區集合。所有這三個策略都重載了構造函數,允許您指定特定分區的起始偏移量。

如果您具有上述選項無法滿足的特定使用者設置需求,則ConsumerStrategy是您可以擴展的公共類。

程序:

package com.liushun;/*

@auth:liushun

@date:2019-06-20 16:03

Spark Streaming整合kafka,使用Driect DStream方式

*/

import org.apache.kafka.clients.consumer.ConsumerRecord;

import org.apache.kafka.common.TopicPartition;

import org.apache.kafka.common.serialization.StringDeserializer;

import org.apache.spark.SparkConf;

import org.apache.spark.api.java.Optional;

import org.apache.spark.api.java.function.Function;

import org.apache.spark.api.java.function.Function2;

import org.apache.spark.api.java.function.PairFunction;

import org.apache.spark.streaming.Durations;

import org.apache.spark.streaming.api.java.JavaDStream;

import org.apache.spark.streaming.api.java.JavaInputDStream;

import org.apache.spark.streaming.api.java.JavaPairDStream;

import org.apache.spark.streaming.api.java.JavaStreamingContext;

import org.apache.spark.streaming.kafka010.ConsumerStrategies;

import org.apache.spark.streaming.kafka010.KafkaUtils;

import org.apache.spark.streaming.kafka010.LocationStrategies;

import scala.Tuple2;

import java.util.*;

import java.util.regex.Pattern;

public class KaFKaUnionStreamingWordCnt {

private static final PatternSPACE = Pattern.compile(" ");

public static void main(String[] args) {

SparkConf sc=new SparkConf().setMaster("local[2]").setAppName("KaFKaUnionStreamingWordCnt");

JavaStreamingContext jssc=new JavaStreamingContext(sc, Durations.seconds(5));//5秒一次

? ? ? ? jssc.checkpoint(".");//設置上一個批次的值存在的目錄,在生產環境中,放在hdfs某個文件下,相對安全些

// 首先要創建一份kafka參數map

? ? ? ? Map kafkaParams =new HashMap();

// 這里是不需要zookeeper節點,所以這里放broker.list

? ? ? ? String brokerslist="10.101.3.3:9092";

String topics ="kafka_streaming";

//Kafka服務監聽端口

? ? ? ? kafkaParams.put("bootstrap.servers",brokerslist);

//指定kafka輸出key的數據類型及編碼格式(默認為字符串類型編碼格式為uft-8)

? ? ? ? kafkaParams.put("key.deserializer", StringDeserializer.class);

//指定kafka輸出value的數據類型及編碼格式(默認為字符串類型編碼格式為uft-8)

? ? ? ? kafkaParams.put("value.deserializer", StringDeserializer.class);

//消費者ID,隨意指定

? ? ? ? kafkaParams.put("group.id","jis");

//指定從latest(最新,其他版本的是largest這里不行)還是smallest(最早)處開始讀取數據

? ? ? ? kafkaParams.put("auto.offset.reset","latest");

//如果true,consumer定期地往zookeeper寫入每個分區的offset

? ? ? ? kafkaParams.put("enable.auto.commit",false);

//Topic分區

? ? ? ? Map offsets =new HashMap<>();

offsets.put(new TopicPartition(topics,0),0L);

Collection topicsSet =new HashSet<>(Arrays.asList(topics.split(",")));

//通過KafkaUtils.createDirectStream(...)獲得kafka數據,kafka相關參數由kafkaParams指定

? ? ? ? try {

JavaInputDStream> kafkaStream = KafkaUtils.createDirectStream(

jssc,

LocationStrategies.PreferConsistent(),

ConsumerStrategies.Subscribe(topicsSet, kafkaParams, offsets)

//也可以用不指定offset的構造函數替換上部的訂閱

//ConsumerStrategies.Subscribe(topicsSet, kafkaParams)

);

//使用map先對InputDStream進行取值操作

? ? ? ? ? ? JavaDStream lines=kafkaStream.map(new Function, String>() {

@Override

? ? ? ? ? ? ? ? public String call(ConsumerRecord consumerRecord)throws Exception {

return consumerRecord.value();

}

});

//再使用flatMap進行行記錄的空格操作

? ? ? ? ? ? JavaDStream words = lines.flatMap(x -> Arrays.asList(SPACE.split(x)).iterator());

JavaPairDStream pairs? = words.mapToPair(s ->new Tuple2<>(s,1));

//狀態保留

? ? ? ? ? ? JavaPairDStream runningCounts = pairs.updateStateByKey(

new Function2, Optional, Optional>() {

@Override

? ? ? ? ? ? ? ? ? ? ? ? public Optional call(List values, Optional state)throws Exception {

Integer updateValue =0;

if (state.isPresent()) {//是否為空

// 如果有值就獲取

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? updateValue = state.get();

}

// 累加

? ? ? ? ? ? ? ? ? ? ? ? ? ? for (Integer value : values) {

updateValue += value;

}

return Optional.of(updateValue);

}

}

);

runningCounts.print();

jssc.start();

jssc.awaitTermination();

}catch (InterruptedException e) {

e.printStackTrace();

}

}

}

打包提交到spark集群中

./spark-submit --class com.liushun.KaFKaUnionStreamingWordCnt --master yarn /usr/local/spark-2.1.1-bin-hadoop2.6/bin/SparkStreamTest-1.1-SNAPSHOT.jar

驗證正確性:

到此,我們就結束了對spark與kafak0.10版本以上的整合實驗。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容