Spark论文思想之-离散流处理(Spark Streaming精华详述)

4.1 介绍

  可以说在介绍部分很简单,就是讲将RDD模型运用于大规模流处理。有用的信息很好总结,首先说明大规模流处理的设计与传统流处理设计系统背道而驰,但却在容错方面大有裨益而且与其他类型的系统相比也很有竞争力。实时大规模流处理需求自是不必多说。但其挑战巨大,两个重要的方面是容错和解决落后者。以官方所言,30s的恢复延迟就意味着做重要决策良机的错失。来看看官方怎么对现有流处理系统进行评价的:
  现有的分布式流处理系统在容错和解决落后者方面是有明显局限性的。类似Storm、TimeStream、MapReduce Online和流媒体数据库。他们都是建立在接收每个记录的有状态的运算符,更新内部状态、然后发送新纪录,长期运行的连续运算符模型之上的。虽然很自然,但是容错和缓解落后者却非常困难。由此可见,Spark离散流处理的最大亮点应该就是容错能力和缓解落后者了。
  这种模型通过两种方法来实现容错恢复,节点复制和上游备份(节点缓冲发送的消息,并将其重放到失败节点的新副本)。两者各有坏处,节点复制要消耗2倍的磁盘空间,上游备份要花很久才能恢复,因为整个系统都必须等待失败节点通过运算符重跑数据来重建一系列状态。除此之外,每种方法都没有考虑落后者,并将落后者视为一个错误数据,而这样的行为代价是很大的,因为大数据流中,这种情况很容易发生。而复制系统使用同步协议如Flux来协调复制数据(保持一致性),所以落后者同样会使复制变得缓慢。
  重点来了,D-Streams就是为解决这一挑战的。与长期运行的连续运算符相反,D-Streams将流计算分解成微小时间间隔的无状态的确定性的批计算。举例来说,可以将每1s的数据放入一个间隔中,然后在每个间隔数据上运行MapReduce批计算统计。同样,也在多个时间间隔上滚动计算,并将新的时间间隔的统计结果添加到之前的结果中。以这种方式构造计算带来了两种好处:其一,在给定输入数据的情况下,每个时间间隔的状态是完全确定的,这就不需要同步协议了。其二,新状态和旧数据之间的依赖关系是细粒度可见的。正是这两点,使之具有强大的容错能力,完全秒杀复制和上游备份。
  但是实现D-Stream模型可是难题了,难在哪里?低延时,每个间隔的数据处理时间要足够低才行。显然Hadoop MapReduce是肯定不行的,他利用外部稳定存储(eg:HDFS)而且还要进行复制来在job之间共享数据,况且之前提到过,Hadoop MapReduce启动一个作业进程什么都不干就结束,也需要25秒左右。所以怎么办呢?万能的RDD来了,还是利用RDD模型,将数据保留在内存中,无需进行复制,只需记录构建该数据的操作的沿袭图(lineage chain)即可进行容错恢复。重要的是,利用RDD实现D-Stream,端到端的延迟低于1秒。相信已经可以满足绝大多数实时应用,毕竟产生源数据的跟踪事件的时间尺度要高的多(这里指的是像传感器、设备等这种数据收集器,它们采集数据也是周期进行的)。
  好,延迟的问题解决了,还有容错和缓解落后者。这就用到了D-Stream的确定性:并行恢复丢失节点的状态。也就是说,每当一个节点失败了,分区丢失了,集群其它节点并行计算失败节点的丢失分区。当然还是利用了RDD 沿袭图,所以D-Stream每个时间间隔的状态必须是确定性的。因此,容错恢复很快,不用复制,不用进行上游备份重放。那么,为什么连续处理系统不能也一样进行并行容错恢复呢?这样不也能快吗!问题是连续处理系统需要容错复制,为了保证数据一致性,需要依靠复杂的协议来进行同步。这就使得并行恢复很困难了。同样地方法,D-Stream可以使用推测执行的方式进行落后者缓解。前边文章提到过,这个推测执行是将任务副本在集群其它节点执行,谁先执行完就用谁执行的结果。
  Spark Streaming中实现了D-Stream,构建于Spark上,这一系统能够在100个节点的集群上每秒处理6000万个record,并且只有亚秒级的延迟。而且容错恢复和落后者缓解也能在亚秒级时间完成。而且Spark Streaming的单节点的吞吐率能够和商业流数据库相比拟,并可以线性扩展到100个节点,并且能够比开源的Storm和S4系统要快2-5倍,还能提供他们所缺少的容错恢复。
  重要特性来了,由于Spark Streaming运用了和Spark批处理作业相似的处理模型和数据结构(RDD),这使得流查询和批处理以及交互式计算可以无缝连接。这一特性使得用户可以使用Spark对流做即时查询、或者是将流和计算成RDD的历史数据进行join。这实际上非常有用,这使用户可以使用一个简单的API将之前的离散计算联合起来。

一起来看一个对比图:
在这里插入图片描述

图 4.1 传统连续流处理与离散流对比

  从图中我们可以获取一些信息:对于传统连续流处理模型,每个节点都需要不断的接收流数据,然后更新内部状态,然后再发送流。可以看到,用到了复制,这个主要是用来进行容错的,而复制过程中又用到了同步。同步技术用的是Flux或者是DPC等同步协议,同步是为了保证数据一致性,也就是说如果数据有多个父节点,每个节点看到的数据的顺序必须是一样的。对于离散流处理模型,在每个时间间隔内,接收的数据被跨集群存储为分区数据集,然后又通过确定性的并行计算的操作符作用于其上计算下一个分布式数据集,或代表输出数据,或代表在下一个间隔中被接着处理的一种状态数据。

4.2.1 目标

其设计目标简单粗暴
1、可以支撑成百上千个节点的集群。
2、低开销。
3、秒级延迟。
4、秒级容错恢复和落后者缓解。

4.2.2 先前处理模型

  离散流处理出来之前,大部分的流处理模型都是建立在连续操作符模型的基础上的。流计算被划分成一系列的长期运行的有状态的运算符,每个运算符都在record到达的时候处理他们,更新内部状态(例如一张表追踪一个窗口内的页面浏览量统计)。然后再发送一个record作为响应。
  连续处理的最大好处就是尽量的减少了延迟,但是其运算符的状态特性,与网络上的记录交错产生的非确定性相结合,使得很难有效地提供容错处理。其实,最大的挑战在于如何在丢失的、或者慢节点上重建运算符的状态。先前系统大概用两种方案,一个是复制另一个是上游备份,无论哪种,都是在成本与时间上的权衡。
  有一个问题我想可能还比较模糊,那就是复制的是什么?是接收的流数据还是处理之后的数据?

  大多数的数据库系统用的都是备份(提供容错),将其处理体系图拷贝两份,输入记录会被发送到这两个体系中进行处理。但是仅仅对节点进行复制是不是就够了呢?显然不行!想一想,如果两个copy节点的一个状态操作符要将上游发送过来的两个流进行union,一个节点上到达流的记录是1234,3212这个顺序,而另一个节点上3241,2323这个顺序,那操作符在某个时段内(假设只对2个record进行union)执行完成后将得到两个不同的结果。所以这样是不行的,那怎么办呢?当然是同步,这些系统需要用到像Flux和DPC同步协议。用来保证两个copy节点的下游操作符所看到的上游发送过来的流记录的顺序是一致的。所以就涉及到两个copy节点的相同的操作符之间的协调。所以,利用复制进行容错的同步处理的代价是比较大的。尽管容错恢复能力很好。

  然后上游备份是怎么回事呢?在上游备份中,每个节点都会将checkpoint之后发送的message保留一份。当某个节点挂了之后,会有某个节点替代其角色。这时上游节点重放message给该替代节点,替代节点据此执行一系列的状态运算符操作恢复节点状态,这就带来了很长的恢复时间。TimeStream和MapReduce Online使用的是这一模型。流行的消息队列系统Storm也用的这个方法,但是提供“at-least-once”传送messages,具体取决于用户代码。

  总结来讲,复制容错同步消耗巨大,上游备份容错恢复缓慢,而且两者均不处理落后者。两者在小规模数据处理时表现还行,在大型商业集群上就挑战巨大了。

4.3 离散流(D-Streams)

  首先,离散流模型是将流构造成一组短的、无状态的、确定性的任务,而不是连续状态运算符。任务会将时间间隔内的流数据(或计算状态)作为RDD跨任务存储在内存里,这些RDD能够被确定性地重计算得到,供容错使用。将计算分解成短任务的好处是,一来能提供细粒度的依赖,二来可以运用强大的恢复技术例如并行恢复和推测执行。除了容错以外,D-Stream还有其他好处,那就是强大的统一批处理。

在这里插入图片描述

图 4.2 Spark Streaming系统的高层级视图

  图中可以看到,输入系统的数据被分解成批(RDD)然后保存在Spark内存中,之后系统启动批处理作业来处理保存的这些RDD,作业由Spark执行引擎调度。

4.3.1 计算模型

  首先,Spark Streaming是将流计算当做一个小的时间间隔内的、确定性的一组批处理来对待。每个间隔内收到的数据都被当做RDD跨集群存储形成输入数据集。然后当这个间隔时间到的时候,这个输入数据集就会被确定性的并行运算符处理。例如map、reduce、groupby等。处理的结果要么作为程序的输出,要么作为中间状态结果。前者通过分布式的方式存储到外部系统中,后者会被存储为RDD。这个中间状态的数据集会和下一批输入数据一起参与计算,并产生一个新的更新了中间状态的数据集。
  据此可知,Spark Streaming系统是基于离散流模型实现的。在Spark Streaming API中,用户可以通过操纵D-Stream对象来定义程序。一个D-Stream是由一系列的可通过确定性的转换进行处理的、不可变的分区数据集组成。这些转换产生新的D-Stream,并且可能产生RDD形式的中间状态数据。

通过一张图来给出说明:
在这里插入图片描述

图4.3 页面浏览统计程序RDD的沿袭图

  可以看到,图中的每个椭圆代表一个RDD,每个圆圈代表一个分区。每个按时间间隔顺序(竖着看)组成的RDD序列就形成一个D-Stream。Spark Streaming通过利用Scala语言提供功能性的API来向用户公开D-Stream。
pageViews = readStream("http://...", "1s")
ones = pageViews.map(event => (event.url, 1))
counts = ones.runningReduce((a, b) => a + b)

  此代码通过HTTP读取事件流来创建名为pageViews的D-Stream,并将它们分组为1秒间隔。 然后它转换事件流以获得一个名为ones的(URL,1&#x

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值