Spark Streaming技术内幕及源码剖析
上QQ阅读APP看书,第一时间看更新

2.3 Spark Streaming整体架构

Spark Streaming将流式计算分解成一系列短小的批处理作业。这里的批处理引擎是Spark Core,也就是把Spark Streaming的输入数据按照Batch Interval分成一段一段的数据(DStream),每一段数据都转换成Spark中的RDD,然后将Spark Streaming中对DStream的转换操作变为针对Spark中对RDD的转换操作,将RDD经过操作变成中间结果保存在内存中。整个流式计算根据业务的需求可以对中间的结果进行叠加或者将结果存储到外部设备。图2-8显示了Spark Streaming的整体架构。

图2-8 Spark Streaming的整体架构

在Spark Streaming架构中,还要解决好容错性问题。

对于流式计算来说,容错性至关重要。首先要介绍一下Spark中RDD的容错机制。每一个RDD都是一个不可变的分布式可重算的数据集,其记录着确定性的操作继承关系(lineage),所以只要输入数据是可容错的,那么任意一个RDD的分区出错或不可用,都是可以利用原始输入数据通过转换操作而重新算出的。

对于Spark Streaming来说,其RDD的传承关系如图2-9所示,图中的每一个椭圆形表示一个RDD,椭圆形中的每个圆形代表一个RDD中的一个Partition,图中每一列的多个RDD表示一个DStream(图中有3个DStream),而每一行最后一个RDD则表示每一个Batch Size所产生的中间结果RDD。可以看到图中的每一个RDD都是通过lineage相连接的,由于Spark Streaming的输入数据可以来自磁盘,例如HDFS(多份复制)或是来自网络的数据流(Spark Streaming会将网络输入数据的每一个数据流复制两份到其他的机器)都能保证容错性,所以RDD中任意的Partition出错,都可以并行地在其他机器上将缺失的Partition计算出来。

图2-9 Spark Streaming应用案例中RDD的传承关系

除了以上Spark本身的RDD容错机制,Spark Streaming还有与自身特点相关的容错问题需要解决。Job运行在Spark Cluster之上,系统容错更复杂又至关重要。计算性能不好时,必须能限流和动态地调整资源。特别是在复杂的计算后,要设置检查点(checkpoint)。有时我们希望流进来的数据一定会被处理,而且只处理一次。在处理出现崩溃的情况下,要保证exactly-once的事务语义。这些容错机制会在读者掌握一定的技术后专门在后面的章节中进行详细分析。

Spark Streaming运行在Spark上,和其他Spark应用一样,有Driver、Worker、Executor等部分,如图2-10所示。

图2-10 Spark Stream ng应用架构中的Dr ver和Executor

在Driver中,有Stream Context作为Spark Streaming的入口,Spark Streaming的最终处理实际还是交给Spark Context。DStream Graph处理DStream的依赖关系。Job调度器(Job Scheduler)负责定时生成Job。注意这里的Job不是在Spark Core中提到的Job,而是Spark Streaming中自己定义的。Job Scheduler实际上包含接收器跟踪器(Receiver Tracker)和Job生成器(Job Generator)。Receiver Tracker负责启动、管理各个Receiver及管理各个Receiver接收到的数据。Job Generator才真正具体负责定时生成Job。

在Worker中一般会生成若干个Executor。接收器(Receiver)运行在Executor中,接收的数据通过块生成器(Block Generator)生成块(Block)。在每个批处理时间间隔(Batch Duration)中都有从Driver提交来的Job的业务逻辑在Executor中被执行。

无论是Block数据的生成还是Job的生成,都需要能无休止地周而复始地进行下去。Spark Streaming中设计了一个RecurringTimer类来起到定时器的作用。Executor上的Block Generator和Driver上的Job Generator在启动时,都会启动自己的RecurringTimer对象,以定时触发各自的周期性工作。

分布式环境下远程通信是必须的。Spark 1.6推出了RPCEnv、RPCEndpoint、RPCEndpointRef为核心的新型架构下的RPC通信方式。其具体实现有两种方式,Akka和Netty。Akka是基于Scala的Actor的分布式消息通信系统;Netty是一个Java开源框架,提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。目前Spark默认的RPC通信方式是Netty。Spark Streaming中,Driver端的Receiver Tracker、Job Generator以及Executor端的ReceiverSupervisorImpl都会用到这种分布式通信方式。

除了RPC消息循环体,还使用了EventLoop这样的本地消息循环体。有必要对EventLoop做个总结。在生成EventLoop对象时,还会实例化EventLoop对象中的一个消息队列、一个Thread成员。EventLoop获得的消息会放入消息队列中。而这个线程则不断检查消息队列中是否有消息,有则用onReceive方法处理。具体的处理需要在外部实例化该EventLoop时覆盖onReceive方法。Spark Streaming的代码中一般用名为processEvent的函数来对应。Driver的Job Scheduler、Job Generator都使用了EventLoop。

架构中的更多细节以后再深入讨论。