一、简介
Spark Streaming是一种准实时的流式计算框架,它对数据处理的单位是一批而不是一条,在数累积到设置的时间间隔后,对数据进行统一的微批处理。这个时间间隔是Spark Streaming的核心概念和关键参数,直接决定了Spark Streaming作业的数据处理延迟,当然也决定了吞吐量和性能。实时数据的来源可以是:Kafka, Flume, Twitter, ZeroMQ或者TCP sockets,并且可以使用高级功能的复杂算子来处理流数据。例如:map,reduce,join,window 。最终,处理后的数据可以存放在文件系统,数据库等,方便实时展现。
二、底层原理
Spark Streaming中基本的抽象是离散流(DStream),DStream代表的是一个连续的数据流,它的底层实际上是由一系列连续的RDD组成的。每个RDD包含确定时间间隔内的数据,这些离散的RDD连在一起,组成了对应的DStream。所以对DStream的操作,实际上都转化成了对一系列的RDD操作。想要学好Spark Streaming,不妨先把Spark最底层的RDD弄明白。
三、执行流程
1、我们在集群中的其中一台机器上提交我们的Application Jar,然后就会产生一个Application,开启一个Driver,然后初始化SparkStreaming的程序入口StreamingContext;
2、Master会为这个Application的运行分配资源,在集群中的一台或者多台Worker上面开启Excuter,executer会向Driver注册;
3、Driver服务器会发送多个receiver给开启的excuter,(receiver是一个接收器,是用来接收消息指定位置传来的消息,在excuter里面运行的时候,其实就相当于一个task任务)
4、receiver接收到数据后,默认每隔200ms就生成一个block块,就是一个rdd的分区,然后这些block块就存储在executer里面
5、receiver产生了这些block块后会把这些block块的信息发送给StreamingContext;
6、StreamingContext接收到这些数据后,会根据一定的规则将这些产生的block块定义成一个rdd,然后将rdd封装成了DStream
7、Spream Streaming的一系列逻辑操作都是在DStream上,最后返回结果
注意以下:
1.所有的代码逻辑完成后要有一个output operation触发类算子 比如:DStream.print();
2. Streaming框架启动后需要手动调用方法,启动后不能再次添加业务逻辑 JavaStreamingContext.start();
3.//等待spark程序被终止 JavaStreamingContext.awaitTermination();
4.JavaStreamingContext.stop() 无参的stop方法将SparkContext一同关闭,等同于stop(true) 但是stop(false),不会关闭SparkContext。
5.JavaStreamingContext停止之后不能再调用start
四、几种算子
1.transformation算子
1)transform:可以通过transform算子,对Dstream做RDD到RDD的任意操作
2)updateStateByKey:
①为SparkStreaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是一个自定义的对象,更新函数也可以是自定义的。
②通过更新函数对该key的状态不断更新,对于每个新的batch而言,SparkStreaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新。
注意:使用到updateStateByKey要开启checkpoint机制和功能,并且持久化按照如下规则:如果batchInterval设置的时间小于10秒,那么10秒写入磁盘一份。如果batchInterval设置的时间大于10秒,那么就会batchInterval时间间隔写入磁盘一份
用途:统计广告点击流量,统计这一天的车流量,统计点击量
3)reduceByKeyAndWindow:
比如说计算每隔20秒,计算最近30秒内的数据,那么窗口的大小就是30秒,滑动时间间隔是20秒。
注意:
①滑动时间间隔和窗口大小必须是batchInterval的整数倍
②比如上面的例子,可以进行优化操作,优化后的window操作要保存状态所以要设置checkpoint路径,没有优化的window操作可以不设置checkpoint路径
2.output operation算子
1) foreachRDD
必须对抽取出来的RDD执行action类算子,代码才能执行。
2)print
打印DStream的当前数据
3)saveAsTextFiles
将数据存到本地的文件目录下
4)saveAsHadoopFiles
将数据保存到hdfs上
四、HA(Driver)
第一:提交任务层面,在提交任务的时候加上选项 --supervise,当Driver挂掉的时候会自动重启Driver。
第二:代码层面,使用JavaStreamingContext.getOrCreate(checkpoint路径,JavaStreamingContextFactory
五、SparkStreaming整合Kafka
1.receiver模式
执行流程:
①SparkStreaming的Executor接收到Kafka传来的数据
②会对数据进行备份到其他的executor上,默认持久化级别是MEMORY_AND_DISK_SER_2
③备份完成后去zookeeper中更新消费偏移量
④向Driver端的receiver Tracker汇报备份数据的位置信息
⑤Driver把task任务分发到Executor上执行并返回执行情况和结果
存在的问题:
①数据丢失问题
当备份完成后向zookeeper中更新偏移量之后,Driver宕机,会导致executor也挂掉,就会存在数据找不到的问题
解决方案:开启WAL机制。在备份数据的时候也同时将数据备份到hdfs上面,同时需要将持久化级别降级到MEMORY_AND_DISK,但是这样做会导致延迟较高
②重复消费问题
当开启WAL机制之后,如果在备份完成之后,提交偏移量到zookeeper之前,Driver挂掉,下次重启之后就会先去消费hdfs上存在的数据,但是这部分的偏移量还没有提交到zookeeper,就还会在被消费一次
解决方案:用driect模式
并行度:
batchInterval/blockInterval
blockInterval来决定的,默认为200ms,假设batchInterval为5s,那么每隔blockInterval就会产生一个block,这里就对应每批次产生RDD的partition,这样5秒产生的这个Dstream中的这个RDD的partition为25个,并行度就是25。
2.driect模式
Driect模式与Receiver模式的最大区别就是偏移量不再提交到zookeeper上,而是由executor自己管理。数据也不再是由kafka发送,是自己去kafka上拉取数据。
并行度:
kafka中topic的partition数决定的。
六、反压机制
默认情况下,Spark Streaming通过receiver以生产者生产数据的速率接收数据,计算过程中会出现批处理时间大于批间隔时间的情况,其中批处理时间为实际计算一个批次花费的时间,批间隔时间是为Streaming设置的批处理时间。这种情况就有可能会造成数据堆积,导致receiver所在的executor内存溢出问题。
通过设置spark.streaming.backpressure.enabled=true(默认false)来开启动态反压机制,根据JobScheduler反馈作业的执行信息来动态调整数据接受速率。当然,启用反压机制也会占用一定的内存资源。
七、相关配置
预写日志:spark.streaming.receiver.writeAheadLog.enable默认false没有开启
blockInterval:spark.streaming.blockInterval默认200ms
Receiver模式接收速率:spark.streaming.receiver.maxRate默认没有设置
Direct模式接受速率:spark.streaming.kafka.maxRatePerPartition默认没有设置
优雅的停止sparkstream :
spark.streaming.stopGracefullyOnShutdown 设置成true
然后用命令kill -15/sigterm driverpid