关键提要
- 在现代系统中,捕获的数据量已经变得越来越大,并且可能会产生一个无序的(但是有效的)无限数据流。在捕获数据的过程中有可能会有未知的延迟,尤其是数据是在一个不可靠的(分布式的)网络环境中进行收集的情况下。
- 大量的数据处理都需要聚合操作,例句计数和连接,这就意味着数据流必须在有限的块中或者“窗口”中进行切分。
- 水印(watermark)是一种用于解决迟到数据问题的概念。当数据处理系统接受到了一个水印时间戳时,它就会假设它不会再接收到比该时间戳更旧的消息。
- Google Dataflow 模型以及对应的 Apache Beam 实现都鼓励用户为了能够更好地理解数据处理的方法,需要问自己四个问题:你在计算什么?事件时间(Event time)在哪里?什么时候进行处理?如何进行改进?
- Apache Beam 项目中包括:概念上统一的 Beam 模型(是什么、在哪里、什么时候、怎么做);使用 Beam 模型 API 来编写数据处理管道的 SDK;以及像 Apache Flink 或 Apache Spark 这种使用现有分布式处理后端来执行数据处理管道的运行程序。
在 QCon San Francisco 2016 大会上,Frances Perry 和 Tyler Akidau 做了一个关于“使用 Apache Beam 进行流数据处理的基础”的主题演讲。 Perry 和 Akidau 都是 Google 的高级工程师,他们在大会上就如何在现代系统中捕获日益增大的、并且可能形成一个无序(但有效的)无限数据流的数据进行了讨论。这对于数据处理系统是一个挑战,对于那些想要从中提取出有意义的即时结果来洞察业务的终端用户们来说也是一个很大的挑战。
例如,从移动端游戏应用程序中捕获正在进行游戏的玩家评分会产生连续的数据流,相关的业务可能需要对这些数据进行挖掘以便分析和提高玩家的保留率或者“用户粘性”。数据不仅是无序的,而且可能还存在未知的延迟,尤其是如果这些数据是通过一个不可靠的网络进行获取的:由于网络故障,收集的数据可能晚到了几秒钟;由于信号丢失数据,可能会晚到几分钟;如果玩家在飞跃大西洋的航线上玩这个游戏,由于没有手机信号,这些数据可能会晚到数小时(或者数天),直到用户的飞机降落才有可能收到相关的数据。
有些数据处理起来就相对简单一些了,比如说,像分词、翻译或者过滤这种属于元素转换类型的数据处理。但是,这种方式中大量的数据需要进行聚合操作,例如计数和连接。这就意味着数据流必须要在聚合之前被分割成有限个数据块,最终经过处理产生一个结果。
这样做的逻辑方法是将数据流按照处理时间窗口进行划分。例如,两分钟或者一个小时分为一个数据块。但是这种处理方法需要克服潜在的迟到数据这一挑战。这样做可能会导致处理数据的上下文、处理时间以及原始事件时间有显著不同,这可能是一些算法的问题。总之,迟到的数据需要重新洗牌回到适当的时间窗口和上下文之中。
虽然这种对迟到数据的重新洗牌在概念上是有意义的,但是实际实现起来却很困难。在理想化的世界里,事件数据会在它产生时就被处理,但是在实际世界中,在事件生成和事件处理之间会存在一个可变的时间偏差,而正式的方法需要对此进行解释和弥补。解决方案就是使用水印(watermark)对事件时间(event-time)过程进行说明。水印在本质上是一个时间戳,当数据处理系统收到一个水印时,它会假定不会再收到比该水印时间戳更旧的消息。一个水印可以是确定的(perfect),比如说它可以从一组静态的日志文件中获取数据,它还可以是启发式的,在这种情况下,系统必须要对给定的时间窗口到达的所有事件进行最佳猜测。
如果一个水印太慢,系统需要等待较晚的数据到达,而流处理操作的计算结果可能会被延迟。如果一个水印的速度太快,则某些数据到达的时间就会较晚,而一个早期的(推断性的)结果就可能需要被更新。事实上,许多现代系统通过一个分布式系统对无限数据流或者无序数据进行收集,因此数据处理系统必须要考虑这些问题。
演讲中大部分的时间都用来讨论现代流处理的挑战,并且使用了 Dataflow 模型,并在 Apache Beam API 中对该模型进行了对应部分的实现,从而提出了四个问题,以便对数据处理时所需的方法进行更好地理解:
- 你在计算什么?
- 事件时间在哪里?
- 什么时候进行处理?
- 如何进行改进?
对于问题“你在计算什么?”,答案可能是元素类型(单元素)处理,可能是一个翻译器或是过滤器,这其实是目前所流行的 MapReduce 范式的一部分,答案也可能是聚合,例如连接或计数,这能被视为 MapReduce 中的 Reduce 部分。这个问题的答案还可能涉及到复合操作:这些操作是由原语组成的,但是我们希望将其视作概念上更简单的高级操作。这个演讲展示了一个使用 Apache Beam API 和伪 Java 代码的例子,这个例子展示了在示例移动手机游戏应用中进行整数计数。 PCollection 抽象表示了一个潜在分布式的、多元素的数据集:我们可以把 PCollection 视为流水线数据,并且 Beam transform 使用 PCollection 对象作为输入和输出。
对于第二个问题“事件时间在哪里?”,典型的解决方法是使用窗口化(windowing)来将数据划分为基于事件时间的有限数据块。这些时间窗口可以是固定的(比如每五分钟一个窗口),也可以是滑动窗口(比如过去的24 小时的每个小时),还可以是基于会话的(基于特定应用程序的活动的发生)。窗口化的概念非常类似于使用复合键( composite key)在批处理中对数据进行分组的概念。
第三个问题“什么时候进行处理?”,这个问题涉及到当得出结果时控制触发器(trigger)的需求。触发器通常是与水印相关的,当看到了水印之后,我们就会认为已经收到了这个事件时间的所有结果,因此可以得出最终的计算结果了。如果使用的是确定的水印,这可能会导致结果的延迟,这是由于系统会等到所有数据元素在发出任何结果之前进行处理。如果使用的是启发式的水印就能够以更及时的方式发出结果,但是对迟到元素的处理可能会带来额外的挑战。
定义触发器的问题可以通过早期或晚期的触发来缓解。早期的触发可以在特定的时间段(比如每分钟)提供一个推断性的结果。晚期的触发能够使得迟到的数据被处理之后对结果进行更新。使用早期触发器和晚期触发器需要考虑的一个关键因素就是第四个问题:“如何对推测性的结果进行改进(或更新)?”有三种策略可供选择:丢弃,简单地丢弃以往所有的推测性结果;积累,更新每一次所要更新的结果;积累或是回滚,更新每次需要更新的结果,但是也会对之前的结果重新产生影响。在实现的简易性与最后所观测到的结果的正确性之间做了很多权衡。我们必须要考虑到,如果下游服务在分布式管道中执行了多个聚合操作,而没有伴随着对更新进行回滚(retracing),在这种情况下可能会计算出错误的结果。
在演讲的最后一部分讨论了Apache Beam 模型的流处理非常“酷”的特性。谈论的第一个特性就是正确性。在分布式流处理中实现正确性一直以来都是极具挑战性的,分布式的本质就意味着数据会以不固定的延迟到达。然而,现代的流处理系统提供了原语,这使得工程师可以在正确性和结果发送延迟之间做出权衡。第二个特性就是能力。Apache Beam API 为工程师们提供了强大的并且相对易于实现的原语和抽象。比如说,可以通过流式(fluent-style)Beam DSL 轻松地对方法和算法进行替换,例如从固定的窗口聚集策略更换为基于会话的策略。这也与第三个有用的特性有关,第三个特性是可组合性,因为在Beam API 中构建新的管道是很容易的,这以便于对新的假设进行测试和对数据进行实验。最后两个特性是灵活性和模块性,这两个特性能够允许开发人员用最少的(并且易于理解的)代码修改来使用不同的方法进行数据处理。
Apache Beam 中的概念源于最开始 2004 年的 MapReduce 的论文,而 Google 对这篇文章又进行了进一步的完善,创建了许多内部系统,诸如 Bigtable 、 Dremel 、 Spanner 和 MillWheel 。尽管 Google 专注于满足这些提供的内部需求,但是公司的工程师在各种会议和学术期刊上还是发表了一系列的论文,这使得围绕这些想法形成了一个充满活力的开源生态系统。这反过来又催生了大量成功的 Apache 开源项目,例如 Hadoop 、 Drill 、 Spark 和 Tez 。2014 年 Google 云平台开始提供 Cloud Dataflow 服务,它是一个全托管的数据流和批处理数据处理服务,它的创建基于 Google 多年的内部数据处理系统的工作经验。Cloud Dataflow 分为两部分:之前讨论的 Dataflow 编程模型、SDK 和用于执行模型的“无旋钮(no-knobs)”管理服务。Google 最终将 Dataflow 模型和 SDK 作为 Apache Beam 项目贡献给了开源社区。
Apache Beam 项目包括以下三部分:
- 概念性 Beam 模型
- 演讲中提到的“是什么、在哪里、什么时候、怎么做”模型
- 编写 Beam 管道的 SDK
- Java SDK
- Python SDK
- 现有分布式处理后端处理的运行程序
- Apache Flink
- Apache Spark
- Google Cloud Dataflow
- Apache Apex
- Apache Gearpump (孵化中)
- 用于本地开发和测试的 Direct Runner
从根本上来讲,Beam 模型试图对这种现代风格的数据处理语义进行概括,并且为数据处理社区中的不同角色提供了三个核心级别上的抽象:终端用户,他们只想用一种他们所熟悉的语言或者他们的组织已经投资的语言来编写数据管道或者转换库;SDK 编写者,他们希望使用新的语言来实现 Beam 的概念;运行程序的编写者,他们使用的是分布式的数据处理环境,并且希望环境能够支持 Beam 管道。值得注意的是,并不是所有的运行程序都能提供相同的能力(尽管很多都在趋同),Apache Beam 项目已经创建了一系列的运行程序比较表格提供了更多详细信息。
更多有关流处理基础的信息可以去阅读Akidau 的文章《 The world beyond batch: Streaming 101 》和《 The world beyond batch: Streaming 102 》。 Apache Beam 官网也提供了许多有用的引用和指南,除此之外 Beam 社区也为用户和开发者们提供了对应的邮件列表:user-subscribe@beam.apache.org 和 dev-subscribe@beam.apache.org。
在 InfoQ 网站上可以找到 Perry 和 Akidau 在 QCon SF 上的完整的演讲视频: Fundamentals of Stream Processing with Apache Beam 。
关于作者
Daniel Bryant 正引领组织和科技领域的变革。他目前的工作内容包括,通过引入更好的需求收集和规划技术增强组织内部敏捷,关注敏捷开发内构架的相关性,促进持续的集成 / 交付。Daniel 目前的技术专长主要是“DevOps”工具,云 / 容器平台,以及微服务实现。他还是伦敦 Java 团体(LJC)的领导者,为开放源码项目提供帮助,为 InfoQ、DZone 和 Voxxed 等知名技术网站撰写文章,并定期出席 QCon、JavaOne 和 Devoxx 等国际会议。
查看英文原文: Exploring the Fundamentals of Stream Processing with the Dataflow Model and Apache Beam
评论