spark記錄(18)SparkStreaming+kafka receiver和directed模式

一、receiver模式

1 、receiver模式原理圖

2 receiver模式理解:

在SparkStreaming程序運行起來後,Executor中會有receiver tasks接收kafka推送過來的數據。數據會被持久化,默認級別爲MEMORY_AND_DISK_SER_2,這個級別也可以修改。receiver task對接收過來的數據進行存儲和備份,這個過程會有節點之間的數據傳輸。備份完成後去zookeeper中更新消費偏移量,然後向Driver中的receiver tracker彙報數據的位置。最後Driver根據數據本地化將task分發到不同節點上執行。

3 receiver模式中存在的問題

當Driver進程掛掉後,Driver下的Executor都會被殺掉,當更新完zookeeper消費偏移量的時候,Driver如果掛掉了,就會存在找不到數據的問題,相當於丟失數據。

4 如何解決這個問題?

開啓WAL(write ahead log)預寫日誌機制,在接受過來數據備份到其他節點的時候,同時備份到HDFS上一份(我們需要將接收來的數據的持久化級別降級到MEMORY_AND_DISK),這樣就能保證數據的安全性。不過,因爲寫HDFS比較消耗性能,要在備份完數據之後才能進行更新zookeeper以及彙報位置等,這樣會增加job的執行時間,這樣對於任務的執行提高了延遲度。

  • receiver模式代碼(見代碼)
/**
 * receiver 模式並行度是由blockInterval決定的
 * @author root
 *
 */
public class SparkStreamingOnKafkaReceiver {
 
    public static void main(String[] args) {
        SparkConf conf = new SparkConf().setAppName("SparkStreamingOnKafkaReceiver")
                .setMaster("local[2]");
        //開啓預寫日誌 WAL機制
        conf.set("spark.streaming.receiver.writeAheadLog.enable","true");
        
        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
          jsc.checkpoint("./receivedata");
        
        Map<String, Integer> topicConsumerConcurrency = new HashMap<String, Integer>();
        /**
         * 設置讀取的topic和接受數據的線程數
         */
        topicConsumerConcurrency.put("t0404", 1);
        
        /**
         * 第一個參數是StreamingContext
         * 第二個參數是ZooKeeper集羣信息(接受Kafka數據的時候會從Zookeeper中獲得Offset等元數據信息)
         * 第三個參數是Consumer Group 消費者組
         * 第四個參數是消費的Topic以及併發讀取Topic中Partition的線程數
         * 
         * 注意:
         * KafkaUtils.createStream 使用五個參數的方法,設置receiver的存儲級別
         */
        JavaPairReceiverInputDStream<String,String> lines = KafkaUtils.createStream(
                jsc,
                "node3:2181,node4:2181,node5:2181",
                "MyFirstConsumerGroup", 
                topicConsumerConcurrency);
        
//        JavaPairReceiverInputDStream<String,String> lines = KafkaUtils.createStream(
//                jsc,
//                "node3:2181,node4:2181,node5:2181",
//                "MyFirstConsumerGroup", 
//                topicConsumerConcurrency/*,
//                StorageLevel.MEMORY_AND_DISK()*/);
        
        
        JavaDStream<String> words = lines.flatMap(new FlatMapFunction<Tuple2<String,String>, String>() { 

            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Iterable<String> call(Tuple2<String,String> tuple) throws Exception {
                return Arrays.asList(tuple._2.split("\t"));
            }
        });
        
          
        JavaPairDStream<String, Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>() {

            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Tuple2<String, Integer> call(String word) throws Exception {
                return new Tuple2<String, Integer>(word, 1);
            }
        });
        
          
        JavaPairDStream<String, Integer> wordsCount = pairs.reduceByKey(new Function2<Integer, Integer, Integer>() { 
            //對相同的Key,進行Value的累計(包括Local和Reducer級別同時Reduce)
            
            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });
        
         
        wordsCount.print(100);
        
        jsc.start();
        jsc.awaitTermination();
        jsc.close();
    }

}
  • receiver的並行度設置

receiver的並行度是由spark.streaming.blockInterval來決定的,默認爲200ms,假設batchInterval爲5s,那麼每隔blockInterval就會產生一個block,這裏就對應每批次產生RDD的partition,這樣5秒產生的這個Dstream中的這個RDD的partition爲25個,並行度就是25。如果想提高並行度可以減少blockInterval的數值,但是最好不要低於50ms。

 

 二、directed

1 Direct模式理解

SparkStreaming+kafka 的Driect模式就是將kafka看成存數據的一方,不是被動接收數據,而是主動去取數據。消費者偏移量也不是用zookeeper來管理,而是SparkStreaming內部對消費者偏移量自動來維護,默認消費偏移量是在內存中,當然如果設置了checkpoint目錄,那麼消費偏移量也會保存在checkpoint中。當然也可以實現用zookeeper來管理。

2 Direct模式並行度設置

Direct模式的並行度是由讀取的kafka中topic的partition數決定的。

3 Direct模式代碼

/**
 * 並行度:
 * 1、linesDStram裏面封裝到的是RDD, RDD裏面有partition與讀取topic的parititon數是一致的。
 * 2、從kafka中讀來的數據封裝一個DStram裏面,可以對這個DStream重分區 reaprtitions(numpartition)
 * 
 * @author root
 *
 */
public class SparkStreamingOnKafkaDirected {

    public static void main(String[] args) {
        
        SparkConf conf = new SparkConf().setMaster("local").setAppName("SparkStreamingOnKafkaDirected");
//        conf.set("spark.streaming.backpressure.enabled", "false");
//        conf.set("spark.streaming.kafka.maxRatePerPartition    ", "100");
        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
        /**
         * 可以不設置checkpoint 不設置不保存offset,offset默認在內存中有一份,如果設置checkpoint在checkpoint也有一份offset, 一般要設置。
         */
        jsc.checkpoint("./checkpoint");
        Map<String, String> kafkaParameters = new HashMap<String, String>();
        kafkaParameters.put("metadata.broker.list", "node1:9092,node2:9092,node3:9092");
//        kafkaParameters.put("auto.offset.reset", "smallest");
        
        HashSet<String> topics = new HashSet<String>();
        topics.add("t0404");
        JavaPairInputDStream<String,String> lines = KafkaUtils.createDirectStream(jsc,
                String.class,  
                String.class,
                StringDecoder.class,
                StringDecoder.class,
                kafkaParameters,
                topics);
        
        JavaDStream<String> words = lines.flatMap(new FlatMapFunction<Tuple2<String,String>, String>() { //如果是Scala,由於SAM轉換,所以可以寫成val words = lines.flatMap { line => line.split(" ")}
            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Iterable<String> call(Tuple2<String,String> tuple) throws Exception {
                return Arrays.asList(tuple._2.split("\t"));
            }
        });
        
        JavaPairDStream<String, Integer> pairs = words.mapToPair(new PairFunction<String, String, Integer>() {

            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Tuple2<String, Integer> call(String word) throws Exception {
                return new Tuple2<String, Integer>(word, 1);
            }
        });
        
        
        JavaPairDStream<String, Integer> wordsCount = pairs.reduceByKey(new Function2<Integer, Integer, Integer>() { //對相同的Key,進行Value的累計(包括Local和Reducer級別同時Reduce)
            
            /**
             * 
             */
            private static final long serialVersionUID = 1L;

            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });
        
        
        wordsCount.print();
        jsc.start();
        jsc.awaitTermination();
        jsc.close();
    }

}

三、相關配置

預寫日誌:

spark.streaming.receiver.writeAheadLog.enable  默認false沒有開啓

blockInterval:

spark.streaming.blockInterval  默認200ms

反壓機制:

spark.streaming.backpressure.enabled  默認false

接收數據速率:

spark.streaming.receiver.maxRate  默認沒有設置

總結:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章