SparkStreaming 高可靠分析

背景

为了理解Spark Streaming提供的语义,让我们记住Spark的RDD的基本容错语义。
1. RDD是一个不可改变的,确定性可重新计算的分布式数据集。每个RDD都会记住在容错输入数据集上使用的确定性操作的谱系来创建它。
1. 如果RDD的任何分区由于工作节点故障而丢失,则可以使用操作沿袭从原始容错数据集重新计算该分区。
1. 假设所有RDD转换都是确定性的,最终转换的RDD中的数据总是相同的,而不管Spark集群中的故障如何。

Spark对容错文件系统(如HDFS或S3)中的数据进行操作。因此,从容错数据生成的所有RDD也是容错的。但是,Spark Streaming并非如此,因为大多数情况下的数据都是通过网络接收的(除非在使用文件系统)。要为所有生成的RDD实现相同的容错属性,接收到的数据将在群集中的工作节点中的多个Spark执行程序之间复制(默认复制因子为2)。这导致系统中有两种情况的数据在发生故障时需要恢复:

  1. 接收和复制的数据 - 这些数据在单个工作者节点的故障中仍然存在,因为其中一个节点上存在其副本。
  2. 收到的数据,但缓冲复制 - 因为这是不复制的,唯一的方法来恢复这个数据是从源再次得到它。

另外我们关注两种节点的失败:
1. Failure of a Worker Node :运行执行程序的任何工作节点都可能失败,并且这些节点上的所有内存数据都将丢失。如果任何接收者在失败的节点上运行,那么他们的缓冲数据将会丢失。
2. Failure of the Driver Node:如果运行Spark Streaming应用程序的驱动程序节点失败,那么显然SparkContext会丢失,所有执行程序及其内存中的数据都将丢失。

流式传输系统的语义往往是通过系统处理每个记录的次数来定义的。系统可以在所有可能的运行条件下提供三种类型的保证(尽管失败等)
- 最多一次:每个记录将被处理一次或根本不处理。
- 至少一次:每个记录将被处理一次或多次。这比最多一次强,因为它确保不会丢失任何数据。但可能有重复。
- 正好一次:每个记录将被处理一次 - 不会丢失数据,也不会处理多次数据。这显然是三个中最强的保障。

基本语义

在任何流处理系统中,广义来讲,处理数据有三个步骤。

  • 接收数据:使用接收器或其他方式从数据源接收数据。
  • 转换数据:使用DStream和RDD转换来转换接收到的数据。
  • 输出数据:最终转换的数据被推送到外部系统,如文件系统,数据库,前端等。

如果流式应用程序必须完全实现端到端一次保证,那么每个步骤都必须提供一个确切的保证。也就是说,每条记录只能被接收一次,只能被转换一次,并被推送到下游系统一次。让我们在Spark Streaming的上下文中理解这些步骤的语义。

  • 接收数据:不同的输入源提供不同的保证。这在下一小节详细讨论。
  • 转换数据:由于RDD提供的保证,所有已收到的数据将被精确处理一次。即使出现故障,只要接收到的输入数据是可访问的,最终转换的RDD将总是具有相同的内容。
  • 输出数据:默认情况下,输出操作至少确保一次语义,因为它依赖于输出操作的类型(幂等性,不是)和下游系统的语义(支持事务与否)。但是用户可以实现自己的事务机制来实现一次语义。

接收数据的语义

文件

如果所有输入数据已经存在于像HDFS这样的容错文件系统中,则Spark Streaming可以随时从任何故障中恢复并处理所有数据。这给出了 完全一次的语义,意味着所有的数据将被精确处理一次

使用基于receive的数据源
  • 可靠的接收器 - 这些接收器只有在确保接收到的数据已被复制后才确认可靠的来源。如果这样的接收机失败,源将不会收到缓冲(未复制)数据的确认。因此,如果接收机重新启动,信号源将重新发送数据,并且没有数据会由于失败而丢失。
  • 不可靠的接收器 - 这种接收器不发送确认,因此当由于工作人员或驱动器故障而失败时,可能会丢失数据。
避免数据丢失的措施

为了避免以前收到的数据丢失,Spark 1.2引入了预写日志,将接收到的数据保存到容错存储中。随着提前写入日志的启用和可靠的接收器,零数据丢失。就语义而言,它至少提供一次保证。

使用Kafka Direct API
  • 在Spark 1.3中,我们引入了一个新的Kafka Direct API,它可以确保所有的Kafka数据只被Spark Streaming接收一次。

- 除此之外,如果您执行完全一次的输出操作,则可以实现端到端的完全一次保证。

输出操作的语义
输出操作(如foreachRDD)至少有一次语义,也就是说,如果工作失败,转换后的数据可能会多次被写入外部实体。虽然这对于使用saveAs***Files操作保存到文件系统是可以接受的 (因为文件将被相同的数据简单地覆盖),但是可能需要额外的努力来实现一次语义。有两种方法。

多次更新:多次尝试总是写入相同的数据。例如,saveAs***Files总是将相同的数据写入生成的文件。

事务更新:所有更新都是以事务方式进行的,因此更新只能以原子方式进行一次。一个办法是这样的。

具体的高可靠措施
  • 通过streamingContext.checkpoint(path-to-directory)设置检查点的目录。这个目录可以在任何与HadoopAPI口兼容的文件系统中设置,它既用作保存流检查点,又用作保存预写日志。
  • 设置SparkConf的属性 spark.streaming.receiver.writeAheadLog.enable为真(默认值是假)。
    在日志被启用以后,所有接收器都获得了能够从可靠收到的数据中恢复的优势。我们建议禁止内存中的复制机制(in-memory replication)
让我们更深入地探讨一下这个问题,弄清预写日志到底是如何工作的。首先,我们重温一下常用的Spark Streaming的架构。
  • 在一个Spark Streaming应用开始时(也就是driver开始时),相关的StreamingContext(所有流功能的基础)使用SparkContext启动接收器成为长驻运行任务。这些接收器接收并保存流数据到Spark内存中以供处理。用户传送数据的生命周期如下图所示(请参考下列图示)。
  • 接收数据(蓝色箭头)——接收器将数据流分成一系列小块,存储到executor内存中。另外,在启用以后,数据同时还写入到容错文件系统的预写日志。
  • 通知driver(绿色箭头)——接收块中的元数据(metadata)被发送到driver的StreamingContext。这个元数据包括:(i)定位其在executor内存中数据位置的块reference id,(ii)块数据在日志中的偏移信息(如果启用了)。
  • 处理数据(红色箭头)——每批数据的间隔,流上下文使用块信息产生弹性分布数据集RDD和它们的作业(job)。StreamingContext通过运行任务处理executor内存中的块来执行作业。
  • 周期性地设置检查点(橙色箭头)——为了恢复的需要,流计算(换句话说,即 StreamingContext提供的DStreams )周期性地设置检查点,并保存到同一个容错文件系统中另外的一组文件中。
    image

当一个失败的driver重启时,下列事情出现(参考下一个图示)。

  • 恢复计算(橙色箭头)——使用检查点信息重启driver,重新构造上下文并重启接收器。
  • 恢复元数据块(绿色箭头)——为了保证能够继续下去所必备的全部元数据块都被恢复。
  • 未完成作业的重新形成(红色箭头)——由于失败而没有处理完成的批处理,将使用恢复的元数据再次产生RDD和对应的作业。
  • 读取保存在日志中的块数据(蓝色箭头)——在这些作业执行时,块数据直接从预写日志中读出。这将恢复在日志中可靠地保存的所有必要数据。
  • 重发尚未确认的数据(紫色箭头)——失败时没有保存到日志中的缓存数据将由数据源再次发送。因为接收器尚未对其确认。

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值