Spark整理(6)

Spark整理(6)

一,SparkStreaming简介

​ SparkStreaming是流式处理框架,是Spark API的扩展,支持可扩展,高吞吐量,容错的实时数据流处理,实时数据的来源可以是:Kafka,Flume,Twitter,ZeroMQ,或者TCP sockets,并且可以使用高级功能的复杂算子来处理流数据。例如:map,reduce,join,window.最终,处理后的数据可以存放在文件系统,数据库等。
在这里插入图片描述

二,SparkStreaming和Storm的区别

  • Storm是纯实时的流式处理框架,SparkStreaming是准实时的处理框架(微批处理)。正是因为如此,SparkStreaming的吞吐要比Storm要高。
  • Storm的事务机制要比SparkStreaming的要完善。
  • Storm支持动态资源调度,(SparkStreaming在 1.2之后也支持)。
  • SparkStreaming擅长复杂的业务处理,Storm不擅长复杂的业务处理,擅长简单的汇总型计算。

三,SparkStreaming工作流程

3.1 工作流程图

这里数据源拿TCP socket举例
在这里插入图片描述

注意:

  • receiver task是 7*24小时一直执行,一直接收数据,将一段时间内接收到的数据保存到batch中。假设bacthinterval为5s,那么会将接收到的数据每隔5秒封装到一个bacth中,batch没有分布式的计算特性,这一个bacth的数据又被封装到一个RDD中,RDD最终封装到一个DStream中。

例如:假设batchinterval为5秒时,每隔5秒通过SparkStreaming将得到的一个DStream,在第6秒的时候计算这5秒的数据,假设执行任务的时间是3秒,那么第6-9秒一边在接收数据,一边在计算任务,9-10只是在计算数据。然后在第11秒的时候重复上面的操作。

  • 如果Job执行的时间大于bacthinterval会有什么样的问题?

如果接收过来的数据设置的级别是仅内存,接收来的数据会越堆积越多,最后可能会导致OOM,如果设置StorgeLevel包含disk,则内存存放不下的数据会溢写到disk,加大延迟。

3.2 SparkStreaming代码
package com.shsxt.spark.stream;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.function.*;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.Time;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import scala.Tuple2;

import java.util.Arrays;

public class Demo {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("wc");

        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));

        JavaReceiverInputDStream<String> lineDStream = jsc.socketTextStream("node03", 1010);

        JavaDStream<String> wordDStream = lineDStream.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterable<String> call(String s) throws Exception {
                System.out.println(s+"================");
                return Arrays.asList(s.split(" "));
            }
        });

        JavaPairDStream<String, Integer> pairDStream = wordDStream.mapToPair(new PairFunction<String, String, Integer>() {

            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });

        JavaPairDStream<String, Integer> reduceByKeyDStream = pairDStream.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

//        reduceByKeyDStream.foreachRDD(new VoidFunction<JavaPairRDD<String, Integer>>() {
//            @Override
//            public void call(JavaPairRDD<String, Integer> pairRDD) throws Exception {
//
//                System.out.println("***************************");
//
//                pairRDD.filter(new Function<Tuple2<String, Integer>, Boolean>() {
//                    @Override
//                    public Boolean call(Tuple2<String, Integer> v1) throws Exception {
//
//                        System.out.println("***********************");
//
//                        return true;
//                    }
//                }).count();
//            }
//        });

        reduceByKeyDStream.print();

        jsc.start();

        jsc.awaitTermination();

    }
}

注意事项:

  • 启动socket server 服务器 : nc -lk 1010(先安装nc服务,命令:yum install -y nc)

  • receiver模式下接收数据,local的模拟线程必须大于等于2,一个线程用来receiver用来接收数据,另一个线程用来执行job。

  • Durations 时间设置就是我们能接收的延迟度。这个需要根据集群的资源情况以及任务的执行情况来调节。

  • 创建JavaStreamingContext有两种方式(SparkConf 和 SparkContext)

  • 所有的代码逻辑完成后要有一个 output operation算子。

  • JavaStreamingContext.start() Streaming框架启动后不能在添加业务逻辑。

  • JavaStreamingContext.stop() 无参的stop方法将SparkContext一同关闭。stop(false),不会关闭SparkContext

  • JavaStreamingContext.stop() 停止之后不能再调用 start.

四,SparkStreaming算子操作

4.1 foreachRDD

output operation算子,必须对抽取出来的RDD执行action类算子,代码才能执行。

package com.shsxt.spark.stream;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;

public class Operate_ForeachRDD {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("foreachRDD");

        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
        JavaReceiverInputDStream<String> socketDStream = jsc.socketTextStream("node03", 1010);

//        socketDStream.print();


        socketDStream.foreachRDD(new VoidFunction<JavaRDD<String>>() {
            @Override
            public void call(JavaRDD<String> stringJavaRDD) throws Exception {

                stringJavaRDD.foreach(new VoidFunction<String>() {
                    @Override
                    public void call(String s) throws Exception {
                        System.out.println(s);
                    }
                });


            }
        });

        jsc.start();
        jsc.awaitTermination();
        jsc.stop();

    }
}
4.2 transform

transform类算子

可以通过transform算子,对DStream做RDD到RDD的任意操作。

package com.shsxt.spark.stream;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.broadcast.Broadcast;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import scala.Tuple2;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * 过滤黑名单
 * transform操作
 * DStream可以通过transform做RDD到RDD的任意操作
 */
public class TransformOperator {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("transformOperator");

        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));

        //模拟黑名单数据
        ArrayList<String> blackList = new ArrayList<>();
        blackList.add("zhangsan");

        //广播黑名单
        Broadcast<ArrayList<String>> nameBroad = jsc.sparkContext().broadcast(blackList);

        //接收socket数据源
        JavaReceiverInputDStream<String> linesDStream = jsc.socketTextStream("node03", 1010);

        JavaPairDStream<String, String> pairNameRDD = linesDStream.mapToPair(new PairFunction<String, String, String>() {
            @Override
            public Tuple2<String, String> call(String s) throws Exception {
                return new Tuple2<String, String>(s.split(" ")[1], s);
            }
        });


        JavaDStream<String> transformRDD = pairNameRDD.transform(new Function<JavaPairRDD<String, String>, JavaRDD<String>>() {
            @Override
            public JavaRDD<String> call(JavaPairRDD<String, String> nameRDD) throws Exception {

                JavaPairRDD<String, String> filter = nameRDD.filter(new Function<Tuple2<String, String>, Boolean>() {
                    @Override
                    public Boolean call(Tuple2<String, String> v1) throws Exception {
                        return !blackList.contains(v1._1);
                    }
                });


                return filter.map(new Function<Tuple2<String, String>, String>() {
                    @Override
                    public String call(Tuple2<String, String> v1) throws Exception {
                        return v1._2;
                    }
                });
            }
        });

        transformRDD.print();


//        pairNameRDD.transform(new Function<JavaPairRDD<String, String>, JavaRDD<String>>() {
//
//            /**
//             * linesDStrea: 1 zhangsan
//             *              2 lisi
//             *pairNameRDD:  zhangsan , 1 zhangsan
//             *              lisi , 2 lisi
//             *nameRDD      zhangsan , 1 zhangsan
//             *              lisi , 2 lisi
//             */
//
//            @Override
//            public JavaRDD<String> call(JavaPairRDD<String, String> nameRDD) throws Exception {
//
//                JavaPairRDD<String, String> rdd = nameRDD.filter(new Function<Tuple2<String, String>, Boolean>() {
//                    @Override
//                    public Boolean call(Tuple2<String, String> tuple2) throws Exception {
//                        return !nameBroad.value().contains(tuple2._1);
//                    }
//                });
//
//                rdd.foreach(new VoidFunction<Tuple2<String, String>>() {
//                    @Override
//                    public void call(Tuple2<String, String> tuple2) throws Exception {
//                        System.out.println(tuple2);
//                    }
//                });
//
//
//                return null;
//            }
//        });

        jsc.start();
        jsc.awaitTermination();
        jsc.stop();
    }
}
4.3 updateStateByKey

transform类算子

updateStateByKey作用:

  • 为SparkStreaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是一个自定义的对象,更新函数也可以是自定义的
  • 通过更新函数对该key的状态不断更新,对于每个新的batch而言,SparkStreaming会在使用uodateStateByKey的时候为已经存在的key进行state的状态更新。

因为SparkStreaming是一批一批处理,通过updateStateBykey算子我们可以对数据进行批与批之间的汇总操作。

  • 使用到updateStateByKey要开启checkpoint机制和功能
  • 多久会将内存中的数据写入到磁盘一份?

如果batchinterval设置的时间小于10秒,那么10秒写入磁盘一份,如果batchinterval设置的时间大于10秒,那么则以batchinterval为间隔写入磁盘。

package com.shsxt.spark.stream;


import com.google.common.base.Optional;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.api.java.function.VoidFunction;
import org.apache.spark.streaming.Durations;
import org.apache.spark.streaming.api.java.JavaDStream;
import org.apache.spark.streaming.api.java.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import scala.Tuple2;

import java.util.Arrays;
import java.util.List;

public class UpdateStateByKey {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("updateStateByKey");

        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
        /**
         * 设置checkpoint目录
         * 多久会将内存中的数据(每一个key所对应的状态)写入到磁盘上一份呢?
         * 如果你的batch interval小于 10秒,那么每隔10秒会将内存中的数据写入到磁盘中
         * 如果你的batch interval大于10秒,那么就以bacth interval为间隔时间写入磁盘
         */
        jsc.sparkContext().setCheckpointDir("./checkpoint");

        JavaReceiverInputDStream<String> jscDStream = jsc.socketTextStream("node03", 1010);

        JavaDStream<String> wordRDD = jscDStream.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterable<String> call(String s) throws Exception {
                return Arrays.asList(s.split(" "));
            }
        });

        JavaPairDStream<String, Integer> mapPairRDD = wordRDD.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });

        JavaPairDStream<String, Integer> updateStateByKeyRDD = mapPairRDD.updateStateByKey(new Function2<List<Integer>, Optional<Integer>, Optional<Integer>>() {

            /**
             * values :经过分组最后 这个Key所对应的value {1,1,1....}
             * state  这个key是本次之前的状态
             * @param values
             * @param state
             * @return
             * @throws Exception
             */
            @Override
            public Optional<Integer> call(List<Integer> values, Optional<Integer> state) throws Exception {

                Integer updateValue = 0;
                if (state.isPresent()) {
                    updateValue = state.get();
                }

                System.out.println(updateValue + "    ====================");

                for (Integer value : values) {
                    updateValue += value;
                }

                return Optional.of(updateValue);
            }
        });

        updateStateByKeyRDD.print();

        updateStateByKeyRDD.foreachRDD(new VoidFunction<JavaPairRDD<String, Integer>>() {
            @Override
            public void call(JavaPairRDD<String, Integer> pairRDD) throws Exception {
                System.out.println(pairRDD);
                pairRDD.collect();
            }
        });

        jsc.start();
        jsc.awaitTermination();
        jsc.stop();
    }
}
4.4 窗口操作
  • 窗口操作理解图
    在这里插入图片描述

如果每隔5秒1个batch,上图中的窗口长度为15s,窗口滑动间隔为10s.

  • 窗口长度和滑动间隔必须是batchinterval的整数倍。如果不是整数被会检测报错

  • 优化窗口操作示意图
    在这里插入图片描述

优化后的窗口操作要保存状态所以要设置chechpoint路径,没有优化的窗口函数可以不设置checkpoint的路径。

package com.shsxt.spark.stream;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function0;
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.*;
import scala.Tuple2;

import java.util.Arrays;

/**
 * 基于滑动创建偶的热点搜索词实时统计
 */
public class WindowOperator {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("windowHotWord");

        JavaStreamingContext jsc = JavaStreamingContext.getOrCreate("./checkPoint", new Function0<JavaStreamingContext>() {
            @Override
            public JavaStreamingContext call() throws Exception {
                System.out.println("新的Driver启动........................");
                return new JavaStreamingContext(conf, Durations.seconds(5));
            }
        });
        /**
         * 没有优化的窗口函数可以不设置checkpoint目录
         * 优化的窗口函数必须设置checkpoint目录
         */
        jsc.sparkContext().setCheckpointDir("./checkPoint");
        jsc.checkpoint("./checkPoint");

        JavaReceiverInputDStream<String> socketDStream = jsc.socketTextStream("node03", 1010);

        JavaDStream<String> linesDStream = socketDStream.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterable<String> call(String s) throws Exception {
                return Arrays.asList(s.split(" "));
            }
        });

        JavaPairDStream<String, Integer> wordPairDStream = linesDStream.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });

//        JavaPairDStream<String, Integer> searchWordCountDStream = wordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
//            @Override
//            public Integer call(Integer v1, Integer v2) throws Exception {
//                return v1 + v2;
//            }
//        }, Durations.seconds(15), Durations.seconds(5));
//
//        searchWordCountDStream.print();

        /**
         * window窗口操作优化
         */
        JavaPairDStream<String, Integer> resultWordCountDStream = wordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        }, new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 - v2;
            }
        }, Durations.seconds(15), Durations.seconds(5));

        resultWordCountDStream.print();


        jsc.start();
        jsc.awaitTermination();
        jsc.stop();


    }
}

五,Driver HA(Standalone或者Mesos)

​ 因为SparkStreaming是7*24小时运行的,Driver只是一个简单的进程,有可能挂掉,Driver挂掉了,那么Executor也就挂掉了(这里指的是集群环境下的情况)。Yarn平台cluster模式下提交任务,ApplicationMaster相当于Driver,如果挂掉会自动启动AM.这里所说的DriverHA针对的是SparkStreaming和Mesos资源调度的情况下。实现Driver的高可用的两个步骤:

1.提交任务层面,在提交任务的时候加上选项 --supervise ,当Driver挂掉的时候会自动重启Driver.

2.代码层面:使用JavaStreamingContext.getOrCreate(checkpoint路径,JavaStreamingContextFactory)

Driver中元数据包括:

  • 创建应用程序的配置信息
  • DStream的操作逻辑
  • Job中没有完成的批次数据,也就是job的执行进度。

代码:

SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("windowHotWord");

		//创建前先去checkpoint文件中查询,不存在数据就新建一个Driver
        JavaStreamingContext jsc = JavaStreamingContext.getOrCreate("./checkPoint", new Function0<JavaStreamingContext>() {
            @Override
            public JavaStreamingContext call() throws Exception {
                System.out.println("新的Driver启动........................");
                return new JavaStreamingContext(conf, Durations.seconds(5));
            }
        });

六,SparkStreaming+Kafka

6.1 receiver模式
  • receiver原理图:
    在这里插入图片描述

  • receiver模式理解

    在SparkStreaming程序运行起来后,Executor中会有receiver tasks接收kafka 推送过来的消息。数据会被持久化,默认持久化的级别是 Memory_AND_DISK_ser_2,这个级别也可以修改。reveiver task会对接收过来数据进行存储和备份,这个过程会有节点之间的数据传输。备份完成后,会去zookeeper中更新消费偏移量,然后向Driver中的receiver tracker汇报数据的位置。最后Driver根据数据本地化将task分发到不同节点上执行。

  • receiver模式存在的问题

当Driver进程挂掉后,Driver下的executor都会被杀掉,当更新完zookeeper消费偏移量的时候,Driver如果挂掉了,就会存在找不到数据的问题,相当于丢失数据。

  • 如何解决数据丢失的问题?

开启WAL(write and log) 预写日志机制,在接收过来数据备份到其他节点的时候,同时备份到HDFS上一份(我们需要将接收来的数据的持久化级别降级到 Memory_AND_DISK_ser),这样就能保证数据的安全性。不过,写HDFS比较消耗性能,要在备份完数据之后才能进行更新偏移量和汇报位置等,这样会增加任务的执行时间,增加了延迟度。

  • receiver模式的并行度设置

receiver的并行度是由 spark.streaming.blockInterval来决定的,默认为200ms,假设batchinterval为5s,那么每隔5秒blockInterval会产生一个block,对应的就是RDD中的一个partition,这样5秒产生的这个DStream中的RDD的partition为25个,并行度就是25。如果向提高并行度可以降低该参数的值,但是最好不要低于50ms.

  • 优化后的重复消费问题
存在重复消费的场景,如果在kafka发送完之后,receiver task 接收到数据后,做完备份数据之后,在向zookeeper更新偏移量的过程中,Driver程序挂掉(这部分数据是未丢失),重新启动后,会先去执行备份数据任务
同时由于偏移量未提交,又重复读取并计算了一次相同数据。
  • 代码
package com.shsxt.spark.stream;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
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.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaPairReceiverInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;
import scala.Tuple2;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class SparkStreamingOnKafkaReceiver {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("receiver");

        //开启WAL预写日志
//        conf.set("spark.streaming.receiver.writeAheadLog.enable","true");

        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
        jsc.checkpoint("./receiverData");

        Map<String,Integer> topicConsumerConcurrency =  new HashMap<String,Integer>();

        /**
         * 设置读取的topic和接收数据的线程数
         */
        topicConsumerConcurrency.put("sk1",1);
        topicConsumerConcurrency.put("sk2",1);

        JavaPairReceiverInputDStream<String,String> topicDStream = KafkaUtils.createStream(
                jsc,
                "node01:2181,node02:2181,node03:2181",
                "receiverTopic",
                topicConsumerConcurrency);

        //  (null,wcb)
        topicDStream.print();



        System.out.println("==============================");

        JavaDStream<String> flatMapRDD = topicDStream.flatMap(new FlatMapFunction<Tuple2<String, String>, String>() {
            @Override
            public Iterable<String> call(Tuple2<String, String> tuple2) throws Exception {
                return Arrays.asList(tuple2._2);
            }
        });

        JavaPairDStream<String, Integer> mapPairRDD = flatMapRDD.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });

        JavaPairDStream<String, Integer> reduceByKeyRDD = mapPairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

        reduceByKeyRDD.print();

        jsc.start();
        jsc.awaitTermination();
        jsc.stop();

    }
}

kafkaProducer.java

package com.shsxt.spark.test;


import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
import kafka.serializer.StringEncoder;

import java.util.Properties;

/**
 *
 */
public class KafkaProducer extends Thread {

    private String topic;  //发送给Kafka的数据
    private Producer<Integer,String> producerForKafka;

    public KafkaProducer(String topic){
        this.topic = topic;

        Properties props = new Properties();
        props.put("metadata.broker.list","node01:9092,node02:9092,node03:9092");
        props.put("serializer.class", StringEncoder.class.getName());
        props.put("acks",1);

        producerForKafka = new Producer<Integer, String>(new ProducerConfig(props));
    }

    @Override
    public void run() {
        int count = 0;
        while (true){
//            count++;
            String value = "wcb";

            KeyedMessage<Integer, String> message = new KeyedMessage<>(topic, value);

            producerForKafka.send(message);
            System.out.println(value+"   ----------------------->");

            //hash partition  当有key时,默认通过key取hash后,对partition_number取余数
            //producerForKafka.send(new KeyedMessage<>(topic,5,value));

//            if (0==count%2){
//                try {
//                    Thread.sleep(1000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//            }

        }

    }

    public static void main(String args[]){
        new KafkaProducer("sk1").start();
        new KafkaProducer("sk2").start();
    }
}
6.2 direct模式
  • direct模式理解

    SparkStreaming+kafka的direct 模式就是将Kafka看做存储数据的一方,然后主动的去拉取数据。消费者的偏移量不采用zookeeper管理,而是SparkStreaming内部对消息者偏移量自动来维护,默认消费的偏移量是存储在内存中,当然如果设置了checkpoint目录,那么消费者偏移量也会保存在checkpoint目录下,消费者的偏移量也可以使用zookeeper来管理。

  • direct模式并行度设置

Direct模式的并行度是由读取的kafka中topic的partition数决定的。

  • direct模式是 exactly-once
    在这里插入图片描述

理解: 有且只有一次(完美)的消费Kafka中的消息

  • 代码
package com.shsxt.spark.stream;


import kafka.serializer.StringDecoder;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.function.FlatMapFunction;
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.JavaPairDStream;
import org.apache.spark.streaming.api.java.JavaPairInputDStream;
import org.apache.spark.streaming.api.java.JavaStreamingContext;
import org.apache.spark.streaming.kafka.KafkaUtils;
import scala.Tuple2;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
 * 并行度:
 * 1.与kafka topic的 partition个输有关  与之对应的是 DStream中RDD的分区数 也就决定了Task的并行度
 * 2.从kafka中读来的数据封装一个DStream里面,可以对这个DStream重分区 repartitions()
 */
public class SparkStreamingOnKafkaDirect {

    public static void main(String args[]){

        SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("direct");

//        conf.set("spark.streaming.backpressure.enabled","true");
//        conf.set("spark.streaming.kafka.maxRatePerPartition","100");

        /**
         * 优雅的退出会先把当前未执行完的Job执行完毕,然后会关闭接收数据源的通道,然后退出程序。
         */
        //设置优雅的退出 sparkStreaming程序  除了设置参数以外   还得敲 kill -15  driverId
        //conf.set("spark.streaming.stopGracefullyOnShutdown","true");


        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(10));

        //可以不设置checkpoint 不设置不保存offset,offset默认在内存中有一份,如果设置了那么在checkpoint目录下也有一份,一般设置在hdfs路径
        jsc.checkpoint("./kafkaDirect");

        Map<String,String> kafkaParameters =  new HashMap<String,String>();
//        kafkaParameters.put("auto.offset.reset","smallset");
        kafkaParameters.put("metadata.broker.list","node01:9092,node02:9092,node03:9092");

        HashSet<String> topicSet = new HashSet<>();
        topicSet.add("sk1");
        topicSet.add("sk2");

        JavaPairInputDStream<String, String> directDStrean = KafkaUtils.createDirectStream(jsc, String.class, String.class,
                StringDecoder.class, StringDecoder.class, kafkaParameters, topicSet);

        JavaDStream<String> flatMapDStream = directDStrean.flatMap(new FlatMapFunction<Tuple2<String, String>, String>() {
            @Override
            public Iterable<String> call(Tuple2<String, String> tuple2) throws Exception {
                return Arrays.asList(tuple2._2);
            }
        });

        JavaPairDStream<String, Integer> pairRDD = flatMapDStream.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });

        JavaPairDStream<String, Integer> reduce = pairRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {

                return v1 + v2;
            }
        });

        reduce.print();

        jsc.start();
        jsc.awaitTermination();
        jsc.stop();


    }
}
6.3 相关配置
  • 预写日志(针对 receiver模式)
spark.streaming.receiver,writeAheadLog.enable  ##默认false没有开启
  • blockInterval(针对receiver模式)
spark.streaming.blockInterval  ##默认200ms  调节sparkStreaming任务并行度
  • 反压机制
spark.streaming.backpressure.enabled  ##默认false  
  • 接收数据的最大速率(每个接收器 )
spark.streaming.receiver.maxRate  ##没有默认值
  • 从kafka每个分区读取数据的最大速率
spark.streaming.kafka.maxRatePerPartition  ##没有默认值
  • 优雅关闭SparkStreaming程序
spark.streaming.stopGracefullyOnShutdown  ##默认值 false ; 如果为true,在JVM关闭时优先关闭而不是立即关闭

想要优雅关闭,除了代码中配置该参数,还需在客户端输入命令:

kill -15 DriverId
SparkConf conf = new SparkConf();
        conf.setMaster("local[2]").setAppName("direct");

        /**
         * 优雅的退出会先把当前未执行完的Job执行完毕,然后会关闭接收数据源的通道,然后退出程序。
         */
        //设置优雅的退出 sparkStreaming程序  除了设置参数以外   还得敲 kill -15  driverId
        conf.set("spark.streaming.stopGracefullyOnShutdown","true");
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值