SparkStreaming和运行模式

SparkStreaming

sparkStreaming是基于spark的流式计算框架,支持可扩展、分布式、高吞吐的准实时流计算,可以支持kafaka、flume、scokets的数据来源,随后进行复杂的算子或者可以自己定义的逻辑处理数据,最后将数据或者结果存储到hdfs或者磁盘上。

sparkStreaming和strom的区别

  1. storm是纯实时流处理,spakstreaming是准实时(微批处理)的处理框架,吞吐量要比storm要高
  2. storm的事务处理要比saprkstreaming要好
  3. sparkstreaming可以进行复杂的计算处理,storm只能进行简单的汇总报告处理
  4. storm支持资源调度(spark1.2也支持)

sparkStreaming流程理解

在这里插入图片描述
客户端进行9999端口进行数据发送 ,sparkstreaming开启端口监听,receiver task来接收数据处理 receiver task是7*24小时一直在执行,一直接受数据,将一段时间内接收来的数据保存到batch中。假设batchInterval为5s,那么会将接收来的数据每隔5秒封装到一个batch中,batch没有分布式计算特性,这一个batch的数据又被封装到一个RDD中最终封装到一个DStream中。DStream的底层还是RDD,包括逻辑处理的算子也和RDD类似。经过一系列逻辑处理后最后要output Operator算子输出结果,相当于RDD中的action算子。
注意:batchinterval假如为5,每隔5秒通过SparkStreaming将得到一个DStream,在第6秒的时候计算这5秒的数据,假设执行任务的时间是3秒,那么第6到9秒在一边接收数据一边在计算,10秒只是在接收数据。然后在第11秒的时候重复上面的操作。
如果job执行的时间大于batchInterval,那么接受过来的数据设置的级别是仅内存,接收来的数据会越堆积越多,最后可能会导致OOM(如果设置StorageLevel包含disk, 则内存存放不下的数据会溢写至disk, 加大延迟 )。这在后面的优化会提到。

sparkStreaming代码运行

简单的wordcount代码

在这里插入代码片/**
 * 1、local的模拟线程数必须大于等于2 因为一条线程被receiver(接受数据的线程)占用,另外一个线程是job执行
 * 2、Durations时间的设置,就是我们能接受的延迟度,这个我们需要根据集群的资源情况以及监控每一个job的执行时间来调节出最佳时间。
 * 3、 创建JavaStreamingContext有两种方式 (sparkconf、sparkcontext)
 * 4、业务逻辑完成后,需要有一个output operator
 * 5、JavaStreamingContext.start()straming框架启动之后是不能在次添加业务逻辑
 * 6、JavaStreamingContext.stop()无参的stop方法会将sparkContext一同关闭,stop(false) 只会关闭SteamContext ,sparkcontext依然存在
 * 7、JavaStreamingContext.stop()停止之后是不能在调用start
 */


public class WordCountOnline {
	@SuppressWarnings("deprecation")
	public static void main(String[] args) throws InterruptedException {
		 
		 final SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("WordCountOnline");
		/**
		 * 在创建streaminContext的时候 设置batch Interval
		 */
//        final JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));


		JavaSparkContext sc = new JavaSparkContext(conf);
		JavaStreamingContext jsc = new JavaStreamingContext(sc, Durations.seconds(5));
//		final JavaSparkContext sparkContext = jsc.sparkContext();



        JavaReceiverInputDStream<String> lines = jsc.socketTextStream("node01", 9999);

        JavaDStream<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
	
			private static final long serialVersionUID = 1L;

			@Override
			public Iterator<String> call(String s) {

				return Arrays.asList(s.split(" ")).iterator();
			}
		});

//		JavaPairDStream<String, Integer> ones = words.mapToPair(x->new Tuple2<>(x,1));

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

        JavaPairDStream<String, Integer> counts = ones.reduceByKey(new Function2<Integer, Integer, Integer>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Integer call(Integer i1, Integer i2) {
				return i1 + i2;
			}
		});
		//outputoperator类的算子   
 	    counts.print();
 		jsc.start();
 		//等待spark程序被终止
 		jsc.awaitTermination();
        jsc.stop();
        System.out.println("stop=====================");
	}
}

sparkStreaming算子

  1. foreachRDD
    output operation算子,必须对抽取出来的RDD执行action类算子,代码才能执行。类似的还有print算子。
 public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf();
        sparkConf.setMaster("local[2]").setAppName("sparkStream");
        JavaSparkContext sparkContext = new JavaSparkContext(sparkConf);
        JavaStreamingContext streamingContext = new JavaStreamingContext(sparkContext, Durations.seconds(5));
        JavaReceiverInputDStream<String> dStream = streamingContext.socketTextStream("node01", 9999);
        JavaPairDStream<String, Integer> counts = dStream.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String s) throws Exception {
                return Arrays.asList(s.split(" ")).iterator();
            }
        }).mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        }).reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });
       counts.foreachRDD(new VoidFunction<JavaPairRDD<String, Integer>>() {
           @Override
           public void call(JavaPairRDD<String, Integer> RDD) throws Exception {
               System.out.println("**********lllllllll************");

               RDD.foreach(new VoidFunction<Tuple2<String, Integer>>() {
                   @Override
                   public void call(Tuple2<String, Integer> tuple2) throws Exception {
                       System.out.println(tuple2);
                   }
               });
           }
       });
       // counts.print();

        streamingContext.start();

        try {
            streamingContext.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        streamingContext.stop();
    }
  1. transform算子
    transformation类算子 可以通过transform算子,对Dstream做RDD到RDD的任意操作。
 public static void main(String[] args) {
        SparkConf sparkConf = new SparkConf();

        sparkConf.setMaster("local[2]").setAppName("sparkStream");

        JavaSparkContext sparkContext = new JavaSparkContext(sparkConf);

        JavaStreamingContext streamingContext = new JavaStreamingContext(sparkContext, Durations.seconds(5));

        JavaReceiverInputDStream<String> dStream = streamingContext.socketTextStream("node01", 9999);

        JavaDStream<String> result = dStream.mapToPair(new PairFunction<String, String, String>() {
            @Override
            public Tuple2<String, String> call(String s) throws Exception {
                return new Tuple2<>(s.split(" ")[1], s);
            }
        }).transform(new Function<JavaPairRDD<String, String>, JavaRDD<String>>() {
            @Override
            public JavaRDD<String> call(JavaPairRDD<String, String> stringStringJavaPairRDD) throws Exception {
                return stringStringJavaPairRDD.map(new Function<Tuple2<String, String>, String>() {
                    @Override
                    public String call(Tuple2<String, String> stringStringTuple2) throws Exception {
                        return stringStringTuple2._2();
                    }
                });
            }
        });
        result.print();
        streamingContext.start();
        try {
            streamingContext.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        streamingContext.stop();
    }

需要注意的是虽然这个算子没有什么意思,但是注意里面重写方法的时候除了RDD逻辑处理以外的代码都是在Driver端执行的,根据需要使用。

  1. updateStateByKey
    transformation算子
    1>为SparkStreaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是一个自定义的对象,更新函数也可以是自定义的。
    2> 通过更新函数对该key的状态不断更新,对于每个新的batch而言,SparkStreaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新。
    3>使用到updateStateByKey要开启checkpoint机制和功能。
    多久会将内存中的数据写入到磁盘一份?
    如果batchInterval设置的时间小于10秒,那么10秒写入磁盘一份。如果batchInterval设置的时间大于10秒,那么就会batchInterval时间间隔写入磁盘一份。
 public static void main(String[] args) {
        SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("UpdateStateByKeyDemo");
        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));
        /**
         * 设置checkpoint目录
         *
         * 多久会将内存中的数据(每一个key所对应的状态)写入到磁盘上一份呢?
         * 	如果你的batch interval小于10s  那么每格10s会将内存中的数据写入到磁盘上
         * 	如果bacth interval 大于10s,那么就以bacth interval为准
         *
         * 这样做是为了防止频繁的写HDFS
         */

// 		jsc.checkpoint("hdfs://shsxt/spark/checkpoint");
        jsc.checkpoint("./checkpoint");

        JavaReceiverInputDStream<String> lines = jsc.socketTextStream("node01", 9999);

        JavaDStream<String> words = lines.flatMap(x-> Arrays.asList(x.split(" ")).iterator());

//        JavaPairDStream<String, Integer> ones = words.mapToPair(x->new Tuple2<>(x,1));

        JavaPairDStream<String, Integer> ones = words.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String s) throws Exception {
                return new Tuple2<>(s, 1);
            }
        });
        JavaPairDStream<String, Integer> counts =
                ones.updateStateByKey(new Function2<List<Integer>, Optional<Integer>, Optional<Integer>>() {
                    private static final long serialVersionUID = 1L;

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

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

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

                        for (Integer value : values) {
                            updateValue += value;
                        }
                        return Optional.of(updateValue);
                    }
                });

        //output operator
        counts.print();

        jsc.start();
        try {
            jsc.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jsc.close();
    }
  1. 窗口操作
    在这里插入图片描述
    假设每隔5s 1个batch,上图中窗口长度为15s,窗口滑动间隔10s。
    窗口长度和滑动间隔必须是batchInterval的整数倍。如果不是整数倍会检测报错。并且在长度和华东间隔要设置连续,不然会少读数据
    没优化时直接是假如是框三个,直接读取三批数据的数据,reducebykey操作,优化后是按窗口滑动,保存上一个窗口的数据状态,在读取新的一批数据加上,在减去之前丢出去的一批数据,优化后的window操作要保存状态所以要设置checkpoint路径,没有优化的window操作可以不设置checkpoint路径。
    代码如下
public static void main(String[] args) {
		SparkConf conf = new SparkConf()
				.setMaster("local[2]")
				.setAppName("WindowHotWord"); 
		
		JavaStreamingContext jssc = new JavaStreamingContext(conf, Durations.seconds(5));
		/**
		 * 设置日志级别为WARN
		 *
		 */
		jssc.sparkContext().setLogLevel("WARN");
		/**
		 * 注意:
		 *  没有优化的窗口函数可以不设置checkpoint目录
		 *  优化的窗口函数必须设置checkpoint目录		 
		 */

//   		jssc.checkpoint("hdfs://node1:9000/spark/checkpoint");
//   		jssc.checkpoint("./checkpoint");
		JavaReceiverInputDStream<String> searchLogsDStream = jssc.socketTextStream("node01", 7777);
		//word	1
		JavaDStream<String> searchWordsDStream = searchLogsDStream.flatMap(new FlatMapFunction<String, String>() {

			private static final long serialVersionUID = 1L;
			@Override
			public Iterator<String> call(String t) throws Exception {
                System.out.println(t + "*************");
                return Arrays.asList(t.split(" ")).iterator();
			}
		});
		// 将搜索词映射为(searchWord, 1)的tuple格式
		JavaPairDStream<String, Integer> searchWordPairDStream = searchWordsDStream.mapToPair(

				new PairFunction<String, String, Integer>() {

					private static final long serialVersionUID = 1L;

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

				});
		/**
		 * 每隔10秒,计算最近60秒内的数据,那么这个窗口大小就是60秒,里面有12个rdd,在没有计算之前,这些rdd是不会进行计算的。
		 * 那么在计算的时候会将这12个rdd聚合起来,然后一起执行reduceByKeyAndWindow操作 ,
		 * reduceByKeyAndWindow是针对窗口操作的而不是针对DStream操作的。
		 */

        JavaPairDStream<String, Integer> searchWordCountsDStream =
            searchWordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
                @Override
                public Integer call(Integer v1, Integer v2) throws Exception {

                    System.out.println(v1 + " : " + v2);
                    return v1 + v2;
                }
            },Durations.seconds(15),Durations.seconds(5));
		
		/**
		 * window窗口操作优化:
		 */
//        JavaPairDStream<String, Integer> searchWordCountsDStream =
//            searchWordPairDStream.reduceByKeyAndWindow(new Function2<Integer, Integer, Integer>() {
//                @Override
//                public Integer call(Integer v1, Integer v2) throws Exception {
//                    System.out.println("v1:" + v1 + " v2:" + v2 + "  ++++++++++");
//                    return v1 + v2;
//                }
//            }, new Function2<Integer, Integer, Integer>() {
//                @Override
//                public Integer call(Integer v1, Integer v2) throws Exception {
//                    System.out.println("v1:" + v1 + " v2:" + v2 + "------------");
//                    return v1 - v2;
//                }
//            }, Durations.seconds(15), Durations.seconds(5));
//
	  	searchWordCountsDStream.print();
		
		jssc.start();
        try {
            jssc.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jssc.close();
	}
  1. saveasFile
    将DStream中的数据存储到本地或者hdfs中
public static void main(String[] args) {

        final SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("saveFile");
        JavaSparkContext sc = new JavaSparkContext(conf);
        JavaStreamingContext jsc = new JavaStreamingContext(sc, Durations.seconds(5));
        JavaReceiverInputDStream<String> lines = jsc.socketTextStream("node01", 9999);
        JavaDStream<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
 
            private static final long serialVersionUID = 1L;

            @Override
            public Iterator<String> call(String s) {

                return Arrays.asList(s.split(" ")).iterator();
            }
        });
        JavaPairDStream<String, Integer> ones = words.mapToPair(new PairFunction<String, String, Integer>() {
            
            private static final long serialVersionUID = 1L;
            @Override
            public Tuple2<String, Integer> call(String s) {

                return new Tuple2<String, Integer>(s, 1);
            }
        });
        JavaPairDStream<String, Integer> counts = ones.reduceByKey((x,y)->x+y);
//        将数据存到hdfs上面
//        counts.saveAsHadoopFiles();
        DStream<Tuple2<String, Integer>> dstream = counts.dstream();
        //将数据存到本地的文件目录下...
        dstream.saveAsTextFiles("./data/prefix","suffix");
        jsc.start();
        try {
            jsc.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jsc.stop();
    }

Driver HA(Standalone或者Mesos)

因为SparkStreaming是7*24小时运行,Driver只是一个简单的进程,有可能挂掉,所以实现Driver的HA就有必要(如果使用的Client模式就无法实现Driver HA ,这里针对的是cluster模式)。Yarn平台cluster模式提交任务,AM(AplicationMaster)相当于Driver,如果挂掉会自动启动AM。这里所说的DriverHA针对的是Spark standalone和Mesos资源调度的情况下。实现Driver的高可用有两个步骤:
第一:提交任务层面,在提交任务的时候加上选项 --supervise,当Driver挂掉的时候会自动重启Driver。
第二:代码层面,使用JavaStreamingContext.getOrCreate(checkpoint路径,JavaStreamingContextFactory)
Driver中元数据包括:

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

SparkStreaming 和kafaka在整合时的两种模式

  1. receiver模式
    原理图
    在这里插入图片描述
    在spark中启动receiver task任务从kafaka中拉取数据,拉过来后启动持久化备份到其他节点,并将数据读取偏移量offset存储到zookeeper上,在节点上的数据位置会被汇报给Driver,Driver根据数据所在的位置来分发task到相应的节点计算,体现了计算向数据端移动的思想,task结果在汇报给driver端。

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

如何解决这个问题?
开启WAL(write ahead log)预写日志机制,在接受过来数据备份到其他节点的时候,同时备份到HDFS上一份(我们需要将接收来的数据的持久化级别降级到MEMORY_AND_DISK 1),这样就能保证数据的安全性。不过,因为写HDFS比较消耗性能,要在备份完数据之后才能进行更新zookeeper以及汇报位置等,这样会增加job的执行时间,这样对于任务的执行提高了延迟度。这样总体来看的话就要看集群的稳定性了
但是这样会导致数据重复消费问题,先将数据存储到hdfs上后,再将数据偏移量存储到zookeeper上之前这个时间点,这样的话driver重启后,会先去hdfs上的数据做计算,然后再去zookeeper上读取偏移量,这样会造成重复消费。

receiver的并行度设置
receiver的并行度是由spark.streaming.blockInterval来决定的,默认为200ms,假设batchInterval为5s,那么每隔blockInterval就会产生一个block,这里就对应每批次产生RDD的partition,这样5秒产生的这个Dstream中的这个RDD的partition为25个,并行度就是25个task。如果想提高并行度可以减少blockInterval的数值,但是最好不要低于50ms。

 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(10));
//        jsc.checkpoint("./receivedata");
        Map<String, Integer> topicConsumerConcurrency = new HashMap<String, Integer>();
        /**
         * 设置读取的topic和接受数据的线程数
         */
        topicConsumerConcurrency.put("sk1", 1);
        topicConsumerConcurrency.put("sk2", 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,
//				StorageLevel.MEMORY_AND_DISK());

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




        JavaDStream<String> words = lines.flatMap(new FlatMapFunction<Tuple2<String, String>, String>() {

            private static final long serialVersionUID = 1L;

            public Iterator<String> call(Tuple2<String, String> tuple) throws Exception {
                return Arrays.asList(tuple._2.split("\t")).iterator();
            }
        });

        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((x,y)->x+y);
        wordsCount.print();
        jsc.start();
        try {
            jsc.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jsc.close();
    }
  1. Direct模式

    SparkStreaming+kafka 的Driect模式就是将kafka看成存数据的一方,不是被动接收数据,而是主动去取数据。消费者偏移量也不是用zookeeper来管理,而是SparkStreaming内部对消费者偏移量自动来维护,默认消费偏移量是在内存中,当然如果设置了checkpoint目录,那么消费偏移量也会保存在checkpoint中。当然也可以实现用zookeeper来管理。
    Direct模式的并行度是由读取的kafka中topic的partition数决定的。
public static void main(String[] args) {

		SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("SparkStreamingOnKafkaDirected");
//		conf.set("spark.streaming.backpressure.enabled", "true");
//		conf.set("spark.streaming.kafka.maxRatePerPartition", "100");
//        conf.set("spark.streaming.stopGracefullyOnShutdown","true");
		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", "node01:9092,node02:9092,node03:9092");
//		kafkaParameters.put("auto.offset.reset", "smallest");

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

        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>() {
            @Override
            public Iterator<String> call(Tuple2<String, String> tuple2) throws Exception {
                return Arrays.asList(tuple2._2.split("\t")).iterator();
            }
        });

//		JavaPairDStream<String, Integer> pairs = words.mapToPair(x->new Tuple2<>(x,1));

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

        JavaPairDStream<String, Integer> wordsCount = pairs.reduceByKey((x,y)->x+y);
		wordsCount.print();
		jsc.start();
        try {
            jsc.awaitTermination();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        jsc.close();
	}

3.相关配置
预写日志:
spark.streaming.receiver.writeAheadLog.enable 默认false没有开启

blockInterval:
spark.streaming.blockInterval 默认200ms

反压机制:
spark.streaming.backpressure.enabled 默认false
反压机制是一个动态的过程,假如kafaka输出50000条数据每秒,而sparkStreaming只能处理10000条每秒,这样会有数据堆积,会造成oom,这样反压机制在处理速度小于输入速度时,会提高输入的速度,在处理速度大于输入数据速度的时候,会根据延迟度和自身资源来调制,会降低输入数据速度,上下限值不变。

接收数据速率:
Receiver模式:
spark.streaming.receiver.maxRate 默认没有设置
Direct模式:
spark.streaming.kafka.maxRatePerPartition

优雅的停止sparkstream :
spark.streaming.stopGracefullyOnShutdown 设置成true
kill -15/sigterm driverpid

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值