我所看到的sparkstreaming

SparkStreaming

一.简介

1.sparkstreaming简介

  • sparkstreaming是流式处理框架,是sparkAPI的扩展,支持可扩展,高吞吐量.容错的准实时数据流处理

  • 数据来源可以是:kafka,flume,Twitter,ZeroMQ或者TCP sockets,并且可以使用高级功能的复杂算子来处理流数据

  • 处理后的数据可以存放在文件系统,数据库等,方便实时展现

2.sparkstreaming与storm的区别

  • storm是纯实时的流式处理框架,sparkstreaming是准实时的处理框架(微批处理)

  • 微批处理的吞吐量比storm的高

  • storm的事务机制要比sparkstreaming的完善

  • storm支持动态资源调度

  • sparkstreaming擅长复杂的业务处理,storm不擅长,只擅长简单的汇总型计算

二.sparkstreaming的认识

  • 原理图

  • 理解

    • receiver task是7*24小时一直在执行,一直接受数据,将一段时间内接收来的数据保存到batch中,可以设置batchInterval的参数.

    • 假设batchInterval为5s,那么会将接收来的数据每隔5秒封装到一个batch中,batch没有分布式计算特性,这一个batch的数据又被封装到一个RDD中最终封装到一个DStream中。

    • 每隔5秒通过SparkStreamin将得到一个DStream,在第6秒的时候计算这5秒的数据,假设执行任务的时间是3秒,那么第6~9秒一边在接收数据,一边在计算任务,9~10秒只是在接收数据。

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

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

  • 代码实现

    • 注意事项

      • 启动socket server 服务器:nc –lk 9999

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

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

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

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

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

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

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

    • 源码

      • SparkConf conf = new SparkConf().setMaster("local[2]").setAppName("WordCountOnline");
        /**
        * 在创建streaminContext的时候 设置batch Interval
        */
        JavaStreamingContext jsc = new JavaStreamingContext(conf, Durations.seconds(5));

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


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

        @Override
        public Iterable<String> call(String s) {
                  return Arrays.asList(s.split(" "));
        }
        });

        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(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(false);

三.sparkstreaming的算子

除了spark的算子外,还有以下sparkstreaming自己的几个算子

1.foreachRDD

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

2.transform

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

3.updateStateByKey

  • transform类算子

  • 作用

    • 通过更新函数对该key的状态不断更新,对于每个新的batch而言,SparkStreaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新

    • 为SparkStreaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是一个自定义的对象,更新函数也可以是自定义的。

  • 使用到updateStateByKey要开启checkpoint机制和功能。

4.窗口操作

  • 理解图

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

    • 优化后的window操作要保存状态所以要设置checkpoint路径,没有优化的window操作可以不设置checkpoint路径。

 

5.Driver的HA

  • 因为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中元数据包括:

    • 创建应用程序的配置信息

    • DStream的操作逻辑

    • job中没有完成的批次数据,也就是job的执行进度。

 

四.SparkStreaming2.2(包含以前)+Kafka

1.receiver模式

  • receiver模式原理图

  • 流程理解

    • 在sparkstreaming程序运行起来后,Executor中会有receiver tasks接收kafka推送过来的数据

    • 数据会被持久化,默认级别MEMORY_AND_DISK_SER_2,可以修改

    • receiver对接收过来的数据进行存储和备份

    • 备份完成之后去zookeeper中更新消费偏移量,然后向driver中的receiver tracker汇报数据的位置

    • 最后driver根据数据本地化将task分发到不同节点上执行

  • 存在的问题

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

  • 解决方法

    • 开启WAL预写日志机制,在接受过来数据备份到其他节点时,同时备份到HDFS上一份,这样就能保证数据的安全性

    • 因为写HDFS比较消耗性能,要在备份完数据之后才能进行更新zookeeper以及汇报位置等,这样就增加job的执行时间,提高了对任务执行的延迟度

  • receiver并行度设置

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

2.Driect模式

  • Driect的理解

    • SparkStreaming+kafka 的Driect模式就是将kafka看成存数据的一方,不是被动接收数据,而是主动去取数据。消费者偏移量也不是用zookeeper来管理,而是SparkStreaming内部对消费者偏移量自动来维护,默认消费偏移量是在内存中,当然如果设置了checkpoint目录,那么消费偏移量也会保存在checkpoint中。当然也可以实现用zookeeper来管理。

  • direct模式并行度设置

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

  • 相关配置

    • 预写日志

      • spark.streaming.receiver.writeAheadLog.enable  默认false没有开启
    • blockInterval: receiver模式

      • spark.streaming.blockInterval  默认200ms
    • 反压机制

      • spark.streaming.blockInterval 默认false
    • 接收数据速率

      • receiver模式

        • spark.streaming.receiver.maxRate  默认没有设置
      • Driect模式

        • spark.streaming.kafka.maxRatePerPartition
    • 停止sparkstream

      • spark.streaming.stopGracefullyOnShutdown 设置成true

 

五.总结SparkStreaming2.2(包含以前) + Kafka0.8.2

1.receiver模式

  • receiver模式采用了receiver接收器模式,需要一个线程一直接收数据

  • 将数据接收到executor中,默认存储级别是MEMORY_AND_DISK_SER_2

  • receiver模式自动使用zookeeper管理消费者offset

  • receiver模式底层读取kafka采用High Level Consumer API 实现,这种模式不关心offset,只要数据。

  • l receiver模式当Driver挂掉时,有丢失数据问题,可以开启WAL机制 避免丢失数据,但是开启之后加大了数据处理延迟 ,并且存在数据重复消费风险。

  • receiver模式并行度由spark.streaming.blockInterval=200ms.可以减少这个参数增大并行度,最小不能低于50ms

  • receiver模式不被使用的原因

    • 被动将数据接收到Executor,当有任务堆积时,数据存储问题

    • 这种模式不能手动维护消费者offset

2.Dircet模式

  • direct模式没有使用receiver接收器模式,每批次处理数据直接获取当前批次数据处理

  • direct模式没有使用zookeeper管理消费者offset,使用的是spark自己管理,默认存在内存中,可以设置checkpoint,也会保存到checkpoint中一份

  • direct模式底层读取kafka使用Simple Consumer API,可以手动维护消费者offset

  • direct模式并行度与读取的topic的partition一一对应

  • 可以使用设置checkpoint的方式管理消费者offset,使用StreamingContext.getOrCreate(ckDir,CreateStreamingContext) 恢复 。但是有缺点:

    • 当代码逻辑改变时,无法从checkpoint中来恢复offset

    • 当从checkpoint中恢复数据时,有可能造成重复消费,需要我们写代码来保证数据的输出等

  • 如果代码逻辑改变,就不能使用checkpoint模式管理offset,可以手动维护消费者offset,可以将offset存储到外部系统

六.kafka0.11版本改变

  • kafka0.8.2版本消费者offset存储在zookeeper中,对于zookeeper每次操作代价是很昂贵的(因为zookeeper每次都要同步数据到其他的节点上,这就使得其性能不是很好)而且zookeeper集群时不能扩展写能力的

  • kafka0.11版本默认使用新的消费者API,消费者offset会更新到一个kafka自带的topic【__consumer_offsets】中.以消费者groupid为单位可以查询每个组的消费topic情况:

    • 查看所有消费者组

      • ./kafka-consumer-groups.sh --bootstrap-server node1:9092, node2:9092, node3:9092 --list

    • 查看消费者消费的offset位置信息

      • ./kafka-consumer-groups.sh --bootstrap-server node1:9092, node2:9092, node3:9092 --describe --group MyGroupId

    • 重置消费者组的消费offset信息 ,--reset-offsets –all-topics 所有offset。--to-earliest 最小位置。

      • ./kafka-consumer-groups.sh --bootstrap-server c7node1:9092,c7node2:9092,c7node3:9092 --group MyGroupId --reset-offsets --all-topics --to-earliest --execute

七.sparkstreaming2.3+kafka0.11整合

  • 丢弃了sparkstreaming+kafka的receiver模式

  • 采用了新的消费者API实现,类似于2.2中sparkstreaming读取kafka Direct模式.并行度一样.

  • 因为采用了新的消费者api实现,所有相对于1.6的Direct模式【simple api实现】 ,api使用上有很大差别。未来这种api有可能继续变化

  • 大多数情况下,sparkstreaming读取数据使用LocationStrategies.PreferConsistent 这种策略,这种策略会将分区均匀的分布在集群的Executor之间。如果Executor在kafka 集群中的某些节点上,可以使用 LocationStrategies.PreferBrokers 这种策略,那么当前这个Executor 中的数据会来自当前broker节点。

    如果节点之间的分区有明显的分布不均,可以使用 LocationStrategies.PreferFixed 这种策略,可以通过一个map 指定将topic分区分布在哪些节点中。

  • 新的消费者API可以将kafka中的消息预读取到缓存区中,默认大小64K.默认缓存区在Executor中,加快梳理数据速度.可以通过参数spark.streaming.kafka.consumer.cache.maxCapacity 来增大,也可以通过spark.streaming.kafka.consumer.cache.enabled 设置成false 关闭缓存机制。

  • 关于消费者offset

    • 如果设置了checkpoint,那么offset将会存储在checkpoint中.但是缺点很明显:

      • 当代码逻辑改变时,无法从checkpoint中来恢复offset

      • 当从checkpoint中恢复数据时,有可能造成重复的消费,需要我们写代码来保证数据的输出幂等

    • 依靠kafka来存储消费者offset.kafka中有一个特殊的topic来存储消费者offset.新的消费者API中,会定期自动提交offset,自动提交offset的频率由参数auto.commit.interval.ms 决定,默认5S.为了保证消费者数据的精确性,我们可以将自动提交,改成异步的手动提交消费者offset,这样做的缺点有:

      • offset存储在kafka中由参数offsets.retention.minutes=1440控制是否过期删除,默认是保存一天,如果停机达到时长,存储在kafka中的消费者会被清空,offset也就被清除了

      • 无法保证有且只有依次语义,因为偏移量的提交时异步的,所有你结果的输出依然需要自己实现幂等性

    • 自己存储offset.这样在逻辑处理时,保证数据处理的事务,如果处理数据失败.就不保存offset,处理数据成功则保存offset.这样可以做到精准的处理依次要处理的数据

posted @ 2020-01-02 16:19  数据阮小白  阅读(249)  评论(0编辑  收藏  举报