Spark Streaming序言(流式计算概览)

上半部分已经系统性的介绍了RDD模型及实现,下半部分介绍Spark的D-Streams模型原理及D-Streams模型的实现框架Spark Streaming的具体实现以及在大规模流处理上的应用。文章中的原理章节有部分参考和借鉴了<An Architecture for Fast and General Data Processing on Large Clusters >、<Google-MapReduce>、<Google-File-System>,甚至有些图片素材也源自于它们文章中如:(https://www2.eecs.berkeley.edu/Pubs/TechRpts/2014/EECS-2014-12.pdf)。

在讲D-Streams模型之前我们先来看流式处理。笔者查阅了不少资料,想对流式处理做一个定义,但并没有找到确切权威的定义。笔者基于自己的理解与查阅的资源对流式处理从流的特性和流式处理两个方面做一个简单抽象的说明。流(Stream)的特性从数据来讲具有连续、无边界、瞬时,从处理上讲在分布式环境下对无边界的数据集进行连续不断的计算、聚合、分析的一个持续性过程。

我们进一步抽象流式处理框架重点设计内容点(这里只列举了核心点,还有指标、状态等不讨论,这些点也流式处理框架技术选型的参考点)

调度算法选择、拓扑结构构建

在异构集群环境中目前主流的流式处理框架一般采用DAG(Directed Acyclic Graph)做为调度算法,在调度过程中会考虑数据本地性(移动计算,不移动数据思想)、资源节点分类可选择性、调度公平性、容错性、任务优先级排序等。DAG的拓扑结构分成逻辑结构和物理结构。逻辑DAG对算子转换成Task及对M/R的划分,对Result的定义,形成一个闭环。物理DAG基于逻辑DAG分配M/R、Result处理的机器等。针对Spark详见RDD的介绍,Spark Streaming的Graph最终也会转换为RDD的DAG

流式处理模型

直接决定了此框架的表达能力方式,操作方式、本身的局限性及系统的适应能力、适用业务场景。目前有两种方式

a. Continuous operator processing model,输入数据一旦达到即会处理(代表框架:Apache Storm)

最大的优点是时效性会很强,完全可以做实时,Storm优化后可以做到50ms以内做一次响应,但容错性比较难实现、负载均衡上很难达到理想的效果。(Flink 是Event Process 但也提供了Batch Duration 能力)

b. D-Stream processing model,输入数据以批次的方式组织到一起后再进行一起处理(代表框架Spark Streaming)。

时效性会比Apache Storm差很多,状态管理或者join(跨batch)等操作实现会比较困难,但容错性、负载的实现成本会简单很多。

c. 同时支持上述两种模型,比如Flink流式处理框架。

消息传递

消息传递有三种方式分别为At most once、At least once、Exactly once。对于流式框架来讲,消息传递的本质是数据一致性语义,在Spark之前很多流式框架构一般默认采用At least once传递方式,保证数据不会丢失,但这需要应用程序定制好重复数据的策略。现在SparkStreaming、Storm、Flink、Kafka streams均做到了数据一致性语义,实现了Exactly once传递方式。

低时延

低时延是流处理与批处理最大的区别,也是流处理存在的必要性支撑点。

容错设计

当前开源的主流的流式处理框架有SparkStreaming、Storm、Flink等,这些流式处理框架原理在保持各自的发展方向上也存在很多共性,及各自在不同方向上的难点。其中系统故障恢复和慢任务推测是流式处理框架的共同难点,但采用不同的流处理模型其难点表现不一样。要实现一个大型集群的流处理模型并不容易,这些集群的规模往往可能达到数百个节点。在这种规模下,系统故障和慢节点问题很容易破坏系统的稳定性,同时快速恢复在实际应用场景中会显得更加重要。对于批处理,耗时1分钟甚至5分钟从系统故障或者慢节点中恢复过来,业务上也许是可以接受的,但对于流式处理,是很难接受。

然而,现有的流式框架对系统故障和慢任务的处理能力有限,因为大多数分式布式的流式处理框架都是基于连续操作模型。当前,基于连续操作模型的流式处理框架通过CopyUpstream Backup两种方式来进行恢复。然而,Copy需要更多的硬件资源,Upstream Backup重新计算数据串行重建故障节点状态恢复前整个系统处于堵塞状态,这两种恢复方式不太适合大规模集群。

Spark团队在SparkCore基础上开源了SparkStreaming,一种新的流式处理框架,能够很好的解决上面提到的两个问题。SparkStreaming框架采用D-Streams模型,与连续操作模型不同,D-Streams模型将各种数据流或者运算操作产生的流按时间分割成一系列短时间的无状态、确定性的批计算。

D-Streams模型存在自身的优势 ,同时也存在两个挑战。首先是低时延(怎样才能支持最短的Batch Duration)。传统批处理,如Hadoop,存在这方面的缺陷,Hadoop任务间使用复制、磁盘存储方式保存状态、中间结果。相反,DStream模型基于RDD模型构建,使用内存保存数据,使用lineage恢复,能有效的避免复制。当前,SparkStreaming可以做到低于秒级的端对端的延迟。这已经能够满足大多数数据应用场景了。其次就是从故障和慢任务中快速恢复过来,我们使用DStream每个Batch Duration数据的确定性特性可以对一个故障的节点进行并行恢复。当某个节点失效时,集群中的其它节点都分担并计算出丢失节点所承担所有Task。这样使得恢复速度比Upstream Backup快很多,也没有复制开销。由需要复杂的状态同步协议,简单的复制操作,在连续处理系统中并行恢复也很难实现。

我们简单看下Upstream Backup的原理:采用消息复制(数据冗余)的方式确保消息的高可用,每个Master节点记录自身的状态(连续性操作模型)、数据到缓存并写入到日志文件中,然后把数据同步到slave节点。如,下图所示,A节点会记录自身状态,并记录A节点发给B节点的消息,同时会把这些信息保存在A1中。

在无故障期间,副本节点保持空间状态,数据执行效率高,占用系统资源小。如果B节点出现故障,B的上游A节点重新回放故障窗口期间的消息、状态下发给B1节点对消息重新计算,恢复状态 。这个过程一般比较耗时,而且是串行的 ,整个集群都需要待B1节点恢复到故障前的状态。如下图所示:A节点Replay到B1,再由B1发送给C--->D。这里需要注意在回放的时候 B、C、D三个节点需要支持幂等计算。

D-Streams模型设计的目标是:

  1. 可支持数百个可伸缩的集群节点
  2. 计算之外的开销最小,不需要付出2倍的备份开销
  3. 秒级的延迟
  4. 从故障、慢节点中秒级恢复

D-Streams模型定义:

通过计算构造一组短的、无状态的、确定性任务来替代有状态的连续性操作模型,从而避免了传统流式处理的问题。

通过字段理解D-Streams模型会构造一组任务,这组任务有三个特点。第一是时间轴跨度短,当前可以支持1秒一个周期。第二是数据无状态(这里更是指RDD的不可变性),当前SparkStreaming支持有状模型和无状态模型,这点后面会讲到。第三是算子的确定性,D-Streams模型提供了一系统的API,如Map、Reduce 、GroupBy、Join等。下面会详细介绍。

SparkStreaming计算模型:

         把流计算看作成每一小段时间周期性地进行了一系列的确定性批量计算。每个Batch Duration收到的数据,存储在可靠的介质上组合成一个输入数据集,周期结束后,会对此数据集进行确定性的并行操作,这些操作算子由D-Streams API提供。中间状态可以通过弹性分布式数据集(RDDs)存储,这样可以避免恢复使用lineage产生的计算、数据冗余。SparkStreaming为了错误恢复和慢任务,D-StreamsRDDs要跟踪他们的lineage(在RDD章节会有详细介绍,这是RDD重要知识点)。在每一个分布式的数据集中,Spark以Partioner为维度进行跟踪lineage信息。如果某个节点任务失败或者节点失败,通过重新运算已经存储在可靠存储介质的输入数据构建任务,然后重新计算相应的RDD分区,丢失的分区会以并行的方式在不同的节点上进行重新计算。类似的慢任务、慢节点,也会选择在其它节点上对任务副本进行推测(Speculative task)执行,因为相同任务在不同的节点上执行总会得到相同的结果。D-Streams模型的并行化计算主要体现在分区和时间上:

  1. 同一个Batch Duration 每个节点在每个转换操作(D-Streams APIRDDs API转换 )上会产生多个RDD分区,当节点故障时,我们可以在其它节点上以并行的方式重新计算其分区。
  2. 基于Lineage图以并行的方式重新构建不同timesteps的数据结果或者中间状态。例如:在图中,如果一个节点故障,我们可能会丢失一些timesteps的map算子的输出,在SparkStreaming中不同的timesteps的map输出可以并行重新构建。(timesteps笔者理解是不同时间阶段跨Batch Duration,如滑动窗口类的算子)

从计算模型的介绍中我们可以提炼出SparkStreaming的重点设计要点:稳健的定时器、可靠的Job生成器、可靠的的存储介质(这点目前由分布式文件系统保证默认选择HDFS)、lineage构建、算子转换、故障恢复和慢任务推测并行化执行。

消息时序方面的考虑:

D-Streams将每条消息按其到达SparkStreaming集群的时间存入输入数据集,然后异步把元数据信息发送给Driver侧,用于新批次的RDD可执行数据。这样可以确保系统总是可以及时开始一个新的Batch Duration。有时按消息到达时间对输入数据分组,并不能满足所有业务场景。可能需要对消息事件发生的外部时间戳记录进行分组的需求。这个场景更注重考虑消息达到的顺序、延迟对Batch Duration的影响。传统数据库对不连续性流做连续性分析场景做了详细分析(详情可见论文Continuous Analytics Over Discontinuous Streams)。如:统计M点访问首页的PV数,Batch Duration为60秒,M<=T。消息达到集群可能存在延迟,我们无法保证[M,T+1)分钟这个批次所有消息均已经达到集群的输入数据集。D-Streams提供了两种方法来处理这种情况:

  1. 系统可以在开始每个批次之前等待一小段时间。
  2. 用户程序可以在应用上对晚到的记录进行纠正。T+1时刻过去,应用对T+1消息进行统计。然后,在后面的时间周期里系统可以进一步收集[T,T+1)时刻的消息并计算更新统计结果。例如:在[T,T+5)时刻,时间戳为M的消息均达到了输入数据集,再对 [T,T+1)的消息计算,更新新的统计结果。

流式框架均会考虑消息的时序性,因为任何系统都会有外部延时。D-Streams将计算离散化到小批次数据计算中。提供了滑动窗口算子可以很好的处理此类需求。这里需要注意的是,这里讨论消息时序性问题,更多的是指消息达到流式处理框架集群的时序问题。不讨论分布式系统事件顺序的关系,D-Streams模型认为所有的消息事件都是独立的,不区分时序上的先后顺序,有兴趣的同学可以查找<Time,Clocks,and the Ordering of Events in a Distributed System>论文。

一致性语义的考虑:

传统数据库事务四大特性简称ACID,其中C代表数据一致性。一致性可以确保数据操作前和操作后均处于一致性的状态,DB提供的数据一致性可以确保我们在金融交易场景中程序能正确操作,保证数据的正确性。单库采用一阶段、二阶段提交协议,在分布式系统环境中一阶段、二阶段提交协议做不到真正意义的数据一致性。所以分布式环境数据一致性算法一般采用Paxos、Raft、ZAB等。但是流式处理框架讲的一致性语义和DB中讲的数据一致性语义不同。流式处理框架的一致性语义侧重于消息传递方式,消息传递有三种情况:At most once、At least once 、Exactly once,只有支持Exactly once语义才能称之为满足一致性语义。这对于流式处理框架来进实现Exactly once是一个难点,特别是以记录为基础的流式系统中确保跨节点状态一致性。但D-Streams模型中一致性语义是非常明确的,微批量模式天生就支持Exactly once语义。因为微批量模式下自然时间会被切割为每个批量周期,每个周期输出的RDDs反映当前时间周期内以及以前的时间周期收到的所有输入数据集,同时RDDs不可变。具体讲,由于计算的确定性和每个时间周期的输入数据集命名都不一样,每个输出RDD的计算效果相当于以前的时间周期上所有批量作业已经步调一致性的运行,并且没有落后和失败的任务。

系统架构:

         SparkStreaming的系统架构是经典的Master-Slave结构。SparkStreaming 支持Yarn、Mesos、K8s、Standalone等部署模式,在不同的部署模式下Master-Slave叫法不同,比如Yarn部署模式分别叫Driver-Worker。

Master:跟踪D-Stream lineage (DAG拓扑结构计算),调度Job计算新的RDD分区。

Slave(Worker):采集输入数据,保存输入分区和已计算的RDD,执行任务。

Client:发送数据给Client,如:Kafka、Socket等,SparkStreaming支持两种数据源输入,客户端和周期性地从存储系统中加载数据

当前分布式环境下的主流架构有Master-Slave架构和无中心架构,像Storm、Spark之类的流式计算框架都采用Master-Slave架构。这里不详细论述两种架构完全意义上的优缺点,只是简述彼此的差异。

  1. 无中心架构集群规模可以不断横向扩展,随着集群规模扩大内部通信、Master节点的重新选举带来的业务中断、脑裂等问题的解决都是大的挑战。
  2. Master-Slave架构最大的弊端是当集群扩展到一定规模的时候Master会成为整个集群的瓶颈,从而限制了集群规模和扩展。但Master-Slave架构天生具备良好的调度负载均衡的效果,保持各个Work节点压力均衡。SparkStreaming基于DAG调度+Back Pressure技术能够很好的实现集群内部Work之间负载均衡,充分利用集群的资源。针对异构集群的流式计算环境下数据分布性、数据本地性、作业执行流程等问题,使用DAG调度算法已经是业界很成熟的技术解决方案。比如Hadoop的MapReduce模型,Storm的Tuple调度均使用DAG调度算法。

从架构角度看,SparkStreaming与传统流式处理框架的区别在于,SparkStreaming将计算过程分解为小的、无状态的、确定性的任务。每个任务可以运行在不同的Worker上或者同时运行在多个Worker上。在传统的流式处理框架的固定拓扑结构中,把部分计算转移到另一台机器是一个很大的动作。SparkStreaming 基于微批处理方式,可以很简单地在集群上进行负载均衡(后面会介绍DAG做为负载调度算法的优缺点),应该对故障和启动慢节点恢复。

         SparkStreaming 中的所有状态都存储在RDD容错性数据结构中,由于RDD的逻辑分区Partitioner可被确定性计算出来,它可以被DAG算法分配到任何节点上计算,甚至可以在多个点上并行进地计算。这套系统试图最大限度的提高数据的局部性、灵活性,使得Speculative Task执行和并行恢复成为可能。

应用程序执行:

         Spark Streaming的应用程序从一个或者多个输入流开始执行。系统加载数据的方式有两种,从Client端实时接收记录数据,或者周期性的从外部存储系统加载数据。Spark Streaming遵从移动计算不移动数据的设计原则,由Work节点接收和可靠地存储数据,然后实时同步meta信息给Master侧。从客户端接收数据,D-Streams需要确保新数据在向客户端程序发送确认之前在两个Work节点间被复制。

所有数据在每一个Work节点上被一个块存储管理( BlockGenerator类),同时利用Master上的跟踪器(ReceiverTrcker类)来让各个节点找到数据的位置。由于输入数据块和从块计算得到的RDD的分区是不可变的,因此对块存储的跟踪比较简单。每个数据块(Block)只是简单的给一个唯一ID,并所有拥有这个的ID的Work节点都能够对其进行操作。块存储(ReceivedBlockHandler)将新的数据块存储在内存中,但会以LRU策略丢弃过期的数据, 同时每个Work节点在周期结束时都会向Master节点报告它所接收到的数据块IDs(对应的信息为ReceivedBlockInfo)。

为了确定何时开始一个新的Batch Duration,。Master会启动任务计算这个周期内输出的RDDs,不需要其它任何额外的同步。与其它批处理调度器一样,上个周期任务完成,下个任务周期就开始。Spark Streaming依赖于每个时间间隔内Spark现有批处理的调度器,并加入不少优化:

  1. 对一个单独任务的多个操作进行管道式执行
  2. 基于数据的本地性对各任务进行调度
  3. RDD的各个划分进行了控制,尽量减少在网络中数据的Shuffle。例如:在一个reduceByWindow的操作中,每一个周期内的任务需要从当前周期内增加新的结果,和删除多个周期以前的结果。调度器使用相同的Partitioner规则对不同周期内的状态RDD进行切分,保证在同一个节点的每一个Key的数据在各个Batch Duration上保持一致。这里需要注意一点,不同的Batch Duration,相同的RDD Paritioner并不一定会调度到同一个Work上,此种情况会发生网络数据的Shuffle但会大概率落在同一个Work上。

故障和节点恢复:

         D-Streams的确定性使得可以使用两种有效却不同于常规流式系统的恢复技术来恢复Work节点状态,并行恢复和推测执行。此外,它也简化了Master节点的恢复。

  1. 并行恢复:

         当一个Work失败后,D-Streams模型节点上RDD分片的状态以及运行中的所有任务能够在其它Work并行重新计算。通过异步的方式复制RDD的状态到其它Work,系统会周期性地设置RDDs状态的Checkpoint。如果一个Work失败,Spark Streaming会检查所有丢失的RDD分片,然后启动一个任务从上次的Chcekpoint开始重新计算。可以启动多个任务去计算不同的RDD分片,使得整个集群都能参与恢复,促使用应用程序并行恢复能力达到最大化。可以从lineage中获取到明确的依赖关系,D-Stream在每个Batch Duration中并行地计算RDDs分区以及并行处理每个Batch Duration中相互独立操作。

         在上游备份中,由单台闲置的机器执行恢复操作,然后开始处理新的记录。在系统高负荷的情况下这需要很长时间才能恢复,因为在Recovery过程中新的记录会持续到达。而Spark Streaming可以并行恢复,由集群可用机器参与恢复,RDD的Partitioner决定了其并行度。

  1. Master恢复:

Spark Streaming需要做到7*24小时运行,需要设计良好的Master故障容错方案。通过两个步骤做到了这些,第一步是当开始Batch Duration时可靠地记录计算的状态,第二步是当旧Master故障时,让Workd节点连接到新的Master,并且报告他们的RDD分区。D-Streams能够简化恢复的一个关键方面是,如果一个给定的RDD被计算两次是没有问题的。因为操作是确定性的,任务可以重新计算。SparkStreaing 把将D-Streams的元数据存储在HDFS,主要存储三方面数据:

  1. 用户的D-Streams DAG图以及表明用户代码的Scala的函数对象
  2. 最后的Chcekpoint时间点
  3. 自Checkpoint开始的RDD的ID号,其中检查点通过在每个时序进行重命名来更新的HDFS文件。恢复后,新Master会读取这个文件找到它断开的地方,并重新连接到Work,以便确定哪些RDD分区是在内存中。然后继续处理每一个漏掉的时序。

Spark Streaming 整体结构:

 

Spark Streaming是一个对实时数据流进行处理,具备高吞吐、高容错的流式数据处理框架,扩展了SparkCore API。流式数据比如kafka、flume、socket等。

         Spark Streaming 逻辑架构

现在我们来看Spark Streaming 对以上几点的表达,分别对应DStreamGraph构建DAG、Batch Duration生成JOB、流式数据接受及RDD生成、长时容错。

上图呈现了SparkStreaming各组件的交互与作用,其中核心功能大致分以下四点

  1. DAG 拓扑结构构建,DStream与RDD的映射
  2. JOB管理、高可用
  3. 数据接收、存储、构建RDD
  4. 长时容错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值