1. SparkStreaming简介
SparkStreaming是流式处理框架,是Spark API的扩展,支持可扩展、高吞吐量、容错的准实时数据流处理,实时数据的来源可以是:Kafka, Flume, Twitter, ZeroMQ或者TCP sockets,并且可以使用高级功能的复杂算子来处理流数据。例如:map,reduce,join,window 。最终,处理后的数据可以存放在文件系统,数据库等,方便实时展现。
2. SparkStreaming&Storm的区别
1)Storm是纯实时的流式处理框架,SparkStreaming是准实时的处理框架(微批处理)。因为微批处理,SparkStreaming的吞吐量比Storm要高。
2)Storm 的事务机制要比SparkStreaming的要完善。
3)Storm支持动态资源调度。(spark1.2开始和之后也支持)
4)SparkStreaming擅长复杂的业务处理,Storm不擅长复杂的业务处理,擅长简单的汇总型计算。
3. SparkStreaming流程
*
一批一批的处理数据,每几秒会在每几分处理一批数据,
1)数据源Client发送数据到Socker Server服务器的某个端口
2)在Spark Streaming中的receiver task一直监听Socker Server端口的数据。
3)每隔一段时间将监听到的数据封装成一个batch。(这段时间是自己设置的)
4)batch被封装成一个RDD,然后又被封装成一个DStream。
5)然后通过转换算子进行逻辑处理,变成另外一个DStream。
6)再经过output Opoertor算子(类似触发算子),得到结果。
总结:
Spark Stream的receiver task 会7*24小时一直运行,将接收到的数据保存起来,每隔batchInterval的时间将数据封装成一个batch,再封装成RDD,最后封装成一个DStream。
问题:
假设计算一批数据需要6秒,每5秒接收一批数据,随着时间的增长,数据的堆积会越来越严重,如果数据保存在内存中,就会导致OOM,并且影响性能。(如果设置StorageLevel 包含disk,则内存存放不下的数据就会溢写到disk,加大延迟)
在代码中需要注意的事项:
1)在receiver模式下接收数据,local的模拟线程必须大于等于2,一个线程用来接收数据,另一个线程用来执行job。
2)Durations时间设置就是我们接收的延迟度,这个需要根据集群的资源情况一个任务的执行情况来调节。
3)所有的代码逻辑完成后要有一个Output operation类算子。
4)JavaStreamingContext(),Streaming框架启动后不能再次添加业务逻辑。
5)JavaStreamingContext.stop()无参的stop方法将SparkContext一同关闭,stop(fales),不会关闭SparkContext。
6)JavaStreamingContext.stop()停止后不能再调用start。
4. SparkStreaming算子
1.foreachRDD
作用:
将DStream 转换为一个个底层的RDD。foreachRDD算子之内,获取到的RDD算子之外的代码是在Driver端执行的。必须对抽取出来的RDD执行action类算子,代码才能执行。
在原有的逻辑之外(executor端)添加一些逻辑(在Driver端),每当执行原有的逻辑之前,都执行这个逻辑,不需要触发
注意:
RDD之外的这些代码,是在driver运行的,每启动一个job都会执行这里的代码
RDD的处理,必须最后有触发算子,才能启动整个任务的计算
2.transform
作用:
和transform类型,将DStream的操作转换为RDD进行操作,区别:该api没有返回值
3.updateStateByKey
updateStateByKey是transformation算子
作用:
把之前的计算结果和当前这一批的数据合成
weSparkStreaming中每一个Key维护一份state状态,state类型可以是任意类型的,可以是一个自定义的对象,更新函数也可以是自定义的。
通过更新函数对该key的状态不断更新,对于每一个新的batch而言,SparkStreaming会在使用updateStateByKey的时候为已经存在的key进行state的状态更新
使用到updateStateByKey要开启checkpoint机制和功能
那么问题:多久会将内存中的数据写入到磁盘一份?
1)如果batchInterval设置的时间小于10秒,那么10秒写入磁盘一份。
2)如果batchInterval设置的时间大于10秒,那么就会batchInterval时间间隔写入磁盘一份。
4. 窗口操作
应用场景:
每隔五秒计算一批数据,计算十五秒的数据
reduceByKeyAndWindow()方法的两个参数
windowDuration:
窗户的宽度;必须是这个DStream的批处理间隔的倍数
slideDuration:
窗口的滑动间隔(即,新DStream生成RDDs的时间间隔);必须是这个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优化:
没有优化的窗口函数可以不设置checkpoint目录。
优化的窗口函数必须设置checkpoint目录
每次都是十五秒的值相加,有重复的计算;所以进行优化:
0-15的值减去0-5的值加上15-20的值。
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));
5. saveAsTextFiles算子:保存结果到本地,MySQL等
5.Driver HA
因为SparkStreaming是7*24小时运行,Driver只是一个简单的进程,有可能挂掉,所以实现Driver的HA就有必要(如果使用的Client模式就无法实现Driver HA ,这里针对的是cluster模式)。
Yarn平台cluster模式提交任务,AM(AplicationMaster)相当于Driver,如果挂掉会自动启动AM。这里所说的DriverHA针对的是Spark standalone和Mesos资源调度的情况下。
实现Driver的高可用有两个步骤:
1)提交任务层面,在提交任务的时候加上选项 --supervise,当Driver挂掉的时候会自动重启Driver。
2)代码层面,使用JavaStreamingContext.getOrCreate(checkpoint路径,JavaStreamingContextFactory)
Driver中元数据包括:
创建应用程序的配置信息。
DStream的操作逻辑。
job中没有完成的批次数据,也就是job的执行进度。
6. SparkStreaming+Kafka
1. receiver模式
执行流程
SparkStreaming启动,Executor中的receiver tasks 接收kafka推送过来的数据
数据被持久化到其他节点的Exevutor中形成data数据,默认级别为MEMORY_AND_DISK_SER_2 receiver task提交偏移量到zookeeper中。
将备份的数据位置汇报给Driver的receiver Tracker。Driver中就掌握了数据的位置
Drvier发送task去执行
数据丢失问题
当Driver进程挂掉后,Driver下的Executor都会被杀掉,当更新完zookeeper消费偏移量的时候,Driver如果挂掉了,就会存在找不到数据的问题
解决
开启WAL(write ahead log)(配置为spark.streaming.receiver.writeAheadLog.enable 默认false没有开启)
预写日志机制,在接受过来数据备份到其他节点的时候,同时备份到HDFS上一份。
在driver突发宕机,我们会重启driver,那么driver会先到hdfs上检测未被计算的数据,然后将之先计算,计算完成后,再到zookeeper上面读取偏移量,然后继续往后计算数据。
注意:
这种方式,因为多了一步数据上传到hdfs上面,hdfs又会是内部备份,高延迟,最终会使我们的任务延迟度增大。
优化:
将数据的MEMORY_AND_DISK_SER_2方式改成MEMORY_AND_DISK,不需要额外备份
WAL机制导致重复消费问题
当启动WAL机制时,若在数据存在hdfs后,提交偏移量到zookeeper之前,这个时间点,driver挂掉,则会造成数据重复计算。因为,当前driver重启之前,会先读取hdfs的数据做计算,然后再去zookeeper上面读取偏移量,此时就造成了数据重复读取。
并行度设置
receiver的并行度是由spark.streaming.blockInterval来决定的,默认为200ms,假设batchInterval为5s,那么每隔blockInterval就会产生一个block,这里就对应每批次产生RDD的partition,这样5秒产生的这个Dstream中的这个RDD的partition为25个,并行度就是25。如果想提高并行度可以减少blockInterval的数值,但是最好不要低于50ms。
2. Direct模式
SparkStreaming+kafka 的Driect模式
此种模式,kafka偏移量有spark自己保存在内存中,也通过checkpoint进行持久化,放到磁盘层面
Dstream的分区数等于kafka topic的分区数
并行度设置
Direct模式的并行度是由读取的kafka中topic的partition数决定的。
3. 相关配置
blockInterval:
spark.streaming.blockInterval默认200ms
反压机制:
spark.streaming.backpressure.enabled 默认false
接收数据速率:
Receiver模式:
spark.streaming.receiver.maxRate 默认没有设置
Direct模式:
spark.streaming.kafka.maxRatePerPartition
优雅的停止sparkstream :
spark.streaming.stopGracefullyOnShutdown设置成true
kill -15/sigterm driverpid