一、Spark Streaming概述
1.简介
Spark Streaming 是流式处理框架,是Spark ApI的扩展,支持可扩展、高吞吐量、容错的实时数据流处理。
实时数据的来源:kafka,flume,Twitter,ZeroMQ或者TCP Socket,并且可以使用高级功能的复杂算子,来处理流的数据。
算子有:map,reduce,join,window。最终,处理后的数据可以存放在文件系统,数据库等,方便实时展现。
Spark的各个子框架,都是基于Spark的。Spark Streaming在内部的处理机制是接收实时流处理。并根据一定的batchInterval时间间隔分成一批批的数据。然后通过Spark Engine处理这些数据,最后得到处理的一批批结果。
Spark的各个子框架,都是基于核心Spark的,Spark Streaming在内部的处理机制是,接收实时流的数据,并根据一定的时间间隔拆分成一批批的数据,然后通过Spark Engine处理这些批数据,最终得到处理后的一批批结果数据。对应的批数据,在Spark内核对应一个RDD实例,因此,对应流数据的DStream可以看成是一组RDDs,即RDD的一个序列。通俗点理解的话,在流数据分成一批一批后,通过一个先进先出的队列,然后 Spark Engine从该队列中依次取出一个个批数据,把批数据封装成一个RDD,然后进行处理,这是一个典型的生产者消费者模型,对应的就有生产者消费者模型的问题,即如何协调生产速率和消费速率。
2.Spark中的术语
1.离散流(discretized stream)或者DStream:这是Spark Stream对内部持续的实时数据的抽象描述,即我们处理的一个实时数据流,在Spark Stream中对应Stream实例。
2.批数据(batch data):将实时流数据以时间片为单位进行数据分片。将流处理转换为一时间片为单位的批数据。随着时间的出来这些处理的结果就形成了对应的结果数据流了。
3.时间片或批处理的时间间隔(batch interval):这个只是逻辑上的定量标准。一个时间片作为我们拆分数据流的依据。
4.窗口长度:一个窗口覆盖的流数据的时间长度。必须是批处理时间间隔的长度。
5.滑动时间间隔:前一个窗口到后一个窗口经历的时间长度,必须是批处理时间间隔的配属
6.Input Stream:一个Input DStream是一个特殊的DStream,将Spark Streaming 连接到一个外部的数据源来读取数据。
二、SparkStreaming&Storm
1.Storm是纯实时处理数据,SparkStreaming 准实时处理数据,待微批处理。SparkStreaming吞吐量大于Storm.
2.Storm擅长处理汇总型业务数据,SparkStreaming擅长处理复杂业务处理,SparkStreaming还可以使用core,sql
3.Storm的事务相对完善,SparkStreaming 也比较完善
4.Storm支持动态资源调度,Spark1.2+也支持
- 处理模型及延迟
虽然两框架都提供了可扩展性(scalability)和可容错性(fault tolerance),但是它们的处理模型从根本上说是不一样的。Storm可以实现亚秒级时延的处理,而每次只处理一条event,而Spark Streaming可以在一个短暂的时间窗口里面处理多条(batches)Event。所以说Storm可以实现亚秒级时延的处理,而Spark Streaming则有一定的时延。 - 容错和数据保证
然而两者的代价都是容错时候的数据保证,Spark Streaming的容错为有状态的计算提供了更好的支持。在Storm中,每条记录在系统的移动过程中都需要被标记跟踪,所以Storm只能保证每条记录最少被处理一次,但是允许从错误状态恢复时被处理多次。这就意味着可变更的状态可能被更新两次从而导致结果不正确。
任一方面,Spark Streaming仅仅需要在批处理级别对记录进行追踪,所以他能保证每个批处理记录仅仅被处理一次,即使是node节点挂掉。虽然说Storm的 Trident library可以保证一条记录被处理一次,但是它依赖于事务更新状态,而这个过程是很慢的,并且需要由用户去实现。 - 实现和编程API
Storm主要是由Clojure语言实现,Spark Streaming是由Scala实现。如果你想看看这两个框架是如何实现的或者你想自定义一些东西你就得记住这一点。Storm是由BackType和 Twitter开发,而Spark Streaming是在UC Berkeley开发的。
Storm提供了Java API,同时也支持其他语言的API。 Spark Streaming支持Scala和Java语言(其实也支持Python)。 - 批处理框架继承
Spark Streaming的一个很棒的特性就是它是在Spark框架上运行的。这样你就可以想使用其他批处理代码一样来写Spark Streaming程序,或者是在Spark中交互查询。这就减少了单独编写流批量处理程序和历史数据处理程序。 - 生产支持
Storm已经出现好多年了,而且自从2011年开始就在Twitter内部生产环境中使用,还有其他一些公司。而Spark Streaming是一个新的项目,并且在2013年仅仅被Sharethrough使用(据作者了解)。
Storm是 Hortonworks Hadoop数据平台中流处理的解决方案,而Spark Streaming出现在 MapR的分布式平台和Cloudera的企业数据平台中。除此之外,Databricks是为Spark提供技术支持的公司,包括了Spark Streaming。
虽然说两者都可以在各自的集群框架中运行,但是Storm可以在Mesos上运行, 而Spark Streaming可以在YARN和Mesos上运行。
三、SparkStreaming处理数据
Spark Streaming是7*24小时不间断运行的。SparkStreaming在启动之后会首先启动一个job来进行接收数据,将“一段时间”内接收的数据放在一个batch中,这里的一段时间就是batchInterval ,之后将batch封装到一个RDD中,RDD又被封装到DStream离散流中。
DStream有自己的Transformation类算子,这类算子也是懒执行的,需要DStream的outputOperator类算子触发执行的。
例一:
假设batchinterval=5s,集群处理一批次的数据时3s
启动集群,1-5s只接受数据,6-9s一遍接受数据一遍处理数据,9-10秒只是接受数据,依次轮询执行。
例二:
假设batchinterval=5s,集群处理一批次数据的时间是8s
启动集群,0-5秒只是接收数据5-10秒一遍接收数据一遍处理数据,10-13秒一边接受数据,一边处理数据,13-15秒一遍接受数据一遍处理数据。。。
上述肯定会造成数据的堆积。
解决方式:最好是tatchinterval时间内生成一堆数据,batchinterval时间内处理一批数据。
1、SparkStreaming + Kafka ----Receiver
- 1.流程分析
当SparkStreaming application运行起来的时候,首先executor中会有一个receiver task接收kafka中推送过来的数据,将数据接收过来进行持久化,默认的持久化级别是:Memory_AND_DISK_SER_2,receiver task将数据进行储存和备份,这个过程中,会产生节点之间的数据传输,备份完成后去zookeeper中进行更新偏移量。然后向Driver节点上的receiver tracker汇报数据位置,最后Driver根据数据本地化将task发送到不同的executor上去执行。 - 2.receiver模式
在执行的过程中,receiver task将数据的偏移量更新到zookeeper中,这时Driver节点宕机了,那么Driver下的Executor进程会被kill 掉,Executor可能有一部分数据未处理,就会存在找不到数据的问题,相当于数据丢失。
问题解决:
开启WAL(write ahead log)预写日志机制,当receiver task接收过来的数据接收到时,会首先备份到HDFS上一份(我们需要将就收过来的数据的持久化级别降低到MEMORY_ADN_DISK),并且向zookeeper更新信息,以及汇报位置等。这样解决了找不到的问题单数由于向HDFS中写数据会影响job执行的效率。
receiver模式并行度
接收数据时,每隔spark.streaming.blockInterval=200ms,将接受类的数据封装到一个bLock,batchInterval内生成的这些block组成一个batch.
假设batchInterval=5,默认一个batch25个block,这里的每一个block对应着RDD中的一个partition,并行度默认就是25个。
如何提高并行度?
在batchInterval一定的情况下,减少spark.streaming.blockInterval,可以增多对应RDD的分区数,并行度可以增加,一般blockInterval值不低于20ms. - 3.receiver弊端
1).采用了receiver接收器模式,将数据接收到executor中再进行处理,尤其是在任务有堆积情况下。内存占用很大。
2).底层采用了高阶的API实现消费kafka,这种模式不关心offset,只要数据,针对需要维护offset的场景不适用。
3).存在丢失数据的问题,开启WAL可以避免,但是增加了数据处理的延迟。
2.SparkStreaming + Kafka ----Direct
SparkStreaming 1.6+kafka 0.8.2 Direct模式,没有采用receiver接收器模式,读取kafka中数据并行度与topic中partition的个数是一致的,相当于Receiver模式简化了并行度。这种模式在任务堆积的时候,不需要将数据每批次接收到Executor中,这种模式在底层读取kafka中的数据采用了Simple Consumer API实现,这种API可以对offset,进行操作,这种模式采用spark自己管理消费者offset,默认在内存中,如果设置了checkpoint,在checkpoint中也有一份。
Direct模式和Receiver模式区别:
1.Receiver模式采用了receiver接收器模式,Direct模式没有采用
2.Receiver模式采用了High Level Consumer API模式消费kafka。Driect模式采用的是Simple Consumer API消费数据
3.Receiver采用zookeeper管理offset,Direct模式采用Spark自己管理,将offset存贮在topic中。
4.Direct模式相对于Receiver模式简化了并行度,Driver模式读取topic数据对应的并行度与topic的partition个数相同。
相关配置
预写日志
spark.streaming.receiver.writeAheadLog.enable 默认false没有开启
blockInterval
spark.streaming.blockInterval 默认200ms
反压机制
spark.streaming.backpressure.enabled 默认false
接收数据速率:
spark.streaming.receiver.maxRate 默认没有设置
3.SparkStreaming2.3+kafka0.11 改变
-1)丢弃了SparkStreaming+kafka 的receiver模式。
-2) 采用了新的消费者api实现,类似于1.6中SparkStreaming 读取 kafka Direct模式。并行度一样。
-3) 因为采用了新的消费者api实现,所有相对于1.6的Direct模式【simple api实现】 ,api使用上有很大差别。未来这种api有可能继续变化
-4) kafka中有两个参数:
heartbeat.interval.ms
这个值代表 kafka集群与消费者之间的心跳间隔时间,kafka 集群确保消费者保持连接的心跳通信时间间隔。这个时间默认是3s.这个值必须设置的比session.timeout.ms小,一般设置不大于 session.timeout.ms的1/3。
session.timeout.ms:
这个值代表消费者与kafka之间的session 会话超时时间,如果在这个时间内,kafka 没有接收到消费者的心跳【heartbeat.interval.ms 控制】,那么kafka将移除当前的消费者。这个时间默认是10s。
这个时间是位于 group.min.session.timeout.ms【6s】 和 group.max.session.timeout.ms【300s】之间的一个参数,如果SparkSteaming 批次间隔时间大于5分钟,也就是大于300s,那么就要相应的调大group.max.session.timeout.ms 这个值。
- 大多数情况下,SparkStreaming读取数据使用 A.LocationStrategies.PreferConsistent 这种策略,这种策略会将分区均匀的分布在集群的Executor之间。
B.如果Executor在kafka 集群中的某些节点上,可以使用 LocationStrategies.PreferBrokers 这种策略,那么当前这个Executor 中的数据会来自当前broker节点。
C.如果节点之间的分区有明显的分布不均,可以使用 LocationStrategies.PreferFixed 这种策略,可以通过一个map 指定将topic分区分布在哪些节点中。
6)新的消费者api可以将kafka 中的消息预读取到缓存区中,默认大小为64k。默认缓存区在 Executor 中,加快处理数据速度。可以通过参数 spark.streaming.kafka.consumer.cache.maxCapacity 来增大,也可以通过spark.streaming.kafka.consumer.cache.enabled 设置成false 关闭缓存机制。 - 关于消费者offset
1).如果设置了checkpoint ,那么offset 将会存储在checkpoint中。这种有缺点: 第一,当从checkpoint中恢复数据时,有可能造成重复的消费,需要我们写代码来保证数据的输出幂等。第二,当代码逻辑改变时,无法从checkpoint中来恢复offset.
2).依靠kafka 来存储消费者offset,kafka 中有一个特殊的topic 来存储消费者offset。新的消费者api中,会定期自动提交offset。这种情况有可能也不是我们想要的,因为有可能消费者自动提交了offset,但是后期SparkStreaming 没有将接收来的数据及时处理保存。这里也就是为什么会在配置中将enable.auto.commit 设置成false的原因。这种消费模式也称最多消费一次,默认sparkStreaming 拉取到数据之后就可以更新offset,无论是否消费成功。自动提交offset的频率由参数auto.commit.interval.ms 决定,默认5s。如果我们能保证完全处理完业务之后,可以后期异步的手动提交消费者offset。但是这种将offset存储在kafka中由参数offsets.retention.minutes=1440控制是否过期删除,默认是保存一天,如果停机没有消费达到时长,存储在kafka中的消费者组会被清空,offset也就被清除了。
3).自己存储offset,这样在处理逻辑时,保证数据处理的事务,如果处理数据失败,就不保存offset,处理数据成功则保存offset.这样可以做到精准的处理一次处理数据。
代码
创建主题:
./kafka-topics.sh --zookeeper node4:2181,node2:2181,node3:2181 --create --topic mytopic --parttition 3 --replicati
on-factor 5
生产者生产数据:
./kafka-console-producer.sh --broker-list node1:9092,node2:9092,node3:9092 --topic mytopic
消费者消费数据:
./kafka-console-consumer.sh --bootstrap-server node1:9092,node2:9092,node3:9092 --topic mytopic
4,图解Spark Streaming 中RDD的数据流程
对于Spark Streaming来说,其RDD的传承关系如下图所示,图中的每一个椭圆形表示一个RDD,椭圆形中的每个圆形代表一个RDD中的一个Partition,图中的每一列的多个RDD表示一个DStream(图中有三个DStream),而每一行最后一个RDD则表示每一个Batch Size所产生的中间结果RDD。我们可以看到图中的每一个RDD都是通过lineage相连接的,由于Spark Streaming输入数据可以来自于磁盘,例如HDFS(多份拷贝)或是来自于网络的数据流(Spark Streaming会将网络输入数据的每一个数据流拷贝两份到其他的机器)都能保证容错性,所以RDD中任意的Partition出错,都可以并行地在其他机器上将缺失的Partition计算出来。这个容错恢复方式比连续计算模型(如Storm)的效率更高。
5.DStream编程模型
DStream(Discretized Stream)作为Spark Streaming的基础抽象,它代表持续性的数据流。这些数据流既可以通过外部输入源赖获取,也可以通过现有的Dstream的transformation操作来获得。在内部实现上,DStream由一组时间序列上连续的RDD来表示。每个RDD都包含了自己特定时间间隔内的数据流。如图7-3所示。
对DStream中数据的各种操作也是映射到内部的RDD上来进行的,如图7-4所示,对Dtream的操作可以通过RDD的transformation生成新的DStream。这里的执行引擎是Spark。