工业物联网,车联网和实时欺诈风控的需求正在飞速的发展。越来越多的企业新应用,需要的是快速响应客户需求,并同时学习和适应不断变化的行为模式。同时随着 5G 网络、容器云、高性能存储硬件水平的不断提高,让实时流处理正在拥有越来越广泛的市场前景。
流处理在短时间内就能够对连续生成的数据进行分析产生价值,而无需等待批处理中累积和处理,从摄取到结果的低延迟是流处理技术提供的最为关键的优势。例如对于车载系统的分析反馈,集群性能日志数据的分析告警,金融欺诈风控的精准定位、物联网煤气泄漏事件处理等应用而言,高并发下的 10ms 级别的低延时意味着最关键的商业价值。
流式处理看似简单 : 只需在数据到达时以快速、持续和无限的方式对其进行处理和操作。但实际情况是,大多数企业并没有可以支持到 PB 至 EB 数据量级,并同时满足采集速率、故障恢复能力的实时存储/计算引擎。 随着适合处理批、实时场景的各种定制化存储、计算引擎的出现,在业务不断扩展的过程中,也就无法避免地在企业级别的大数据系统之上堆积复杂性,造成了不小的资源浪费以及运维困难。
流式传输迫使系统设计人员重新思考基本的计算和存储原则。当前的大数据处理系统无论是何种架构都面临一个共同的问题,即:“计算是原生的流计算,而存储却不是原生的流存储” 。Pravega 团队重新思考了这一基本的数据处理和存储规则,为这一场景重新设计了一种新的存储类型,即原生的流存储,命名为”Pravega”,取梵语中“Good Speed”之意。
在 Pravega 之前的流数据处理
在大数据繁荣的早期阶段,MapReduce 兴起,我们可以使用数千台服务器的集群分布式处理大量(TB 至 PB 级别)的数据集。在一个或多个大数据集上运行的这种类型的分布式计算通常被称为批处理作业。批处理作业使各种应用程序能够从原始数据中获得价值,这对于拥有庞大用户数据的企业的成长起到了重要的作用。
对于大型数据集的批处理作业通常具有几分钟到几小时的完成时间,如此长的延迟对于许多应用程序来说并不理想,例如推荐系统,使用最新数据至关重要,但与此同时,处理的精准性也需要保证,即使最小程度的推荐失败也可能最终导致用户离开。加之硬件水平的提升,很快我们开始有了更高的要求。我们希望能够跟上数据产生的步伐得到数据处理的结果,而不是等待数据积累然后才处理。低延迟流处理因此慢慢兴起。我们将其称为流处理,因为传入的数据基本上是事件、消息或样本的连续流。
许多对实时分析感兴趣的公司并不愿意放弃 MapReduce 模型。为了解决延迟限制,一些应用程序开始使用微批(micro-batch)处理方法:在较短时间内累积的较小块上运行作业。以 Apache Spark Streaming 为代表的微批处理会以秒级增量对流进行缓冲,然后在内存中进行计算。这种方式的实际效果非常好,它确实使应用程序能够在更短的时间内获得更高价值。
但由于缓冲机制的存在,微批处理仍然有着较高的延迟,为了满足应用的低延迟需求,原生的流处理平台的研发在近五年中不断涌现,百花齐放。早期的系统包括 S4 和 Apache Storm。Storm 使用成熟,有社区基础,至今仍然被许多企业广泛使用。Heron 是由 Twitter 研发的新一代流处理引擎,与 Storm 兼容的同时性能更优。Apache Samza 和 Kafka Stream 则基于 Apache Kafka 消息系统来实现高效的流处理。
由于批处理和流处理系统使用着不同的框架,企业为同时满足实时和批处理的应用程序,不得不使用两套独立的计算基础架构,计算的结果也同样进入不同的框架以进行查询。Storm 的创始人 Nathan Marz 由此提出了 Lambda 的大数据处理架构(如图 1),将大数据平台分割成了批处理层、流处理层和应用服务层。Lambda 架构遵循读写分离,复杂性隔离的原则,整合了离线计算和实时计算,集成 Hadoop,Kafka,Storm,Spark,Hbase 等各类大数据组件,使得两种处理能够在高容错、低延时和可扩展的条件下平稳运行。
图 1: Lambda 架构
随着技术和架构的演进,近年来,工程师们开始意识到用流和批两个词来区分应用场景,进而给计算框架分类并不合适,两种处理实质上有着许多共同点。在很多场景下,流和批处理应用同一套处理逻辑,却不得不因为框架不同进行重复开发。数据在产生之时就没有所谓批和流的概念,只是我们的处理方式不同才导致了数据属性的不同,进而导致了框架的不同。
流和批本来就应该没有界限!
LinkedIn(Apache Kafka 作者,现 Confluent CEO)的 Jay Kreps 提出了 Kappa 架构,将批处理层、流处理层简化为一致性的流处理。谷歌工程师(Apache Beam 核心人物)Tyler Akidau 提出了 Dataflow 模型则致力于取代谷歌上一代的 MapReduce,将批处理(有限的数据流)视为流处理(无限的数据流)的特例,重新定义大数据处理的原语。Apache Flink 作为新一代流处理框架的翘楚,其设计遵循 Dataflow 模型,从根本上统一了批处理和流处理。而 Apache Spark 也推翻了之前微批处理的设计,推出了 Structured Streaming,使用表和 SQL 的概念进行处理的统一。
有效地提取和提供数据对于流处理应用程序的成功至关重要。由于处理速度和频率的不同,数据的摄取需要通过两种策略进行。在典型的 Lambda 架构中,分布式文件系统(例如 HDFS)负责为批处理应用提供高并发、高吞吐量的数据,而消息队列系统(例如 RocketMQ)负责为流处理应用提供数据临时缓冲,发布/订阅功能,数据不进行长时间的持久化保留。两者无法整合也是目前 Kappa 架构对历史数据处理能力有限的原因。
Pravega 设计宗旨是成为流的实时存储解决方案。应用程序将数据持久化存储到 Pravega 中,Pravega 的 Stream 可以有无限制的数量并且持久化存储任意长时间,使用同样的 Reader API 提供尾读(tail read)和追赶读(catch-up read)功能,能够有效满足两种处理方式的统一。
Pravega 支持仅一次处理 (exactly-once),可在 Kappa 架构上实现链接应用需求,以便将计算拆分为多个独立的应用程序,这就是流式系统的微服务架构。我们所设想的架构是由事件驱动、连续和有状态的数据处理的流式存储-计算的模式(如图 2)。
图 2: 流处理的简单生命周期
通过将 Pravega 流存储与 Apache Flink 有状态流处理器相结合,图 2 中的所有写、处理、读和存储都是独立的、弹性的,并可以根据到达数据量进行实时动态扩展。这使我们所有人都能构建以前无法构建的流式应用,并将其从测试原型无缝扩展到生产环境。拥有了 Pravega,Kappa 架构得以凑齐了最后的拼图,形成了统一存储、统一计算的闭环。
流式存储的要求
我们使用的组件需要为它而设计,以满足我们想实现的需求,不然就会像现今的大数据架构那样,形成复杂性的堆砌。上述内容已经提到,现有的存储引擎同时无法满足两种数据读取的需求。结合实际的应用场景,总结所需要的特性,企业级流存储引擎的实现相当有难度,因为它需要三种看似矛盾的系统功能:
能够将数据视为连续和无限的,而不是有限和静态的
能够通过自动弹性伸缩数据采集、存储和处理能力,与负载保持协调一致,持续快速地交付结果
即使在延迟到达或出现乱序数据的情况下,也能连续交付准确的处理结果
让我们具体深入上述特征,以当今业界应用最广的分布式消息系统 Apache Kafka 作为对比,看看 Pravega 如何以今天存储无法实现的方式实现它们。
将数据视为连续和无限的
Kafka 源于 LinkedIn 的日志采集系统,采用分布式事务日志架构进行持久化层的管理。因此,Kafka 采用添加到文件的末尾并跟踪其内容的方式模拟连续和无限的数据流。然而文件既没有针对此模式进行优化,也受限于本地文件系统的文件描述符以及磁盘容量,因此并非是无限的。对于数据的可靠性,Kafka 使用同步副本(in-sync replica)方式进行,占用了更多的存储的同时也意味着对吞吐率性能的受损。并且它们利用消息头部的 header 记录元数据以构造数据结构,使得它们不像字节序列那样通用。
将这些想法拼接在一起, 我们提出了 Pravega 将从数据的角度支持的连续和无限的特点:
Pravega 的 Stream 是一个命名的、持久的、仅追加的、无限的字节序列
使用低延迟追加尾写并从序列的尾读(tail read/write)
具有来自序列较旧部分的高吞吐追赶读(catch-up read)
基于负载的自动(zero-touch)弹性伸缩特性(scale up/scale down)
Kafka 通过将数据拆分为分区,并独立处理来获得并行性。这种做法由来已久,Hadoop 就使用了分区在 HDFS 和 MapReduce 实现了并行化的批处理。对于流式工作负载,传统的分区有着很大的问题:分区会同时影响读客户端和写客户端。连续处理的读写操作所要求的并行程度通常各不相同,使其链接固定数量的分区就会增加实现复杂性。虽然可以添加分区以进行扩展,但这需要手动更新写客户端、读客户端和存储。代价高昂,也并非动态缩放。
Pravega,专为动态和独立扩展而设计,支持:
许多写客户端同时追加写不相交的数据子集
写入数据依靠路由键(routing key)写入不同的 segment 以保证隔离性
让应用程序为写客户端分配键
当键空间或写客户端发生改变时,对应的存储不能有约束和改变
许多读客户端同时处理不相交的数据子集
读取的数据分区不依赖于写入分区
读取的分区由存储策略控制
使用 segment 概念代替物理的分区,且数量根据摄取流量进行自动连续的更新
连续处理数据生成准确的结果
连续计算要得到准确的结果需要仅一次处理(exactly-once)。而仅一次处理语义对数据存储有着明确的要求,数据写入必须是:
持久化的
有序的
一致的
事务性的
这些关键属性也是存储系统设计中最困难的部分。如果没有事先的设计考虑,后期就只能通过系统重构来完成这些特性。
持久性意味着一旦写入得到确认,即使遇到组件故障数据也不会丢失。持久性由于与失败后数据重放相关因而至关重要。没有持久化的系统意味着数据需要开发人员进行手动归档,将永久副本存储在归档系统(通常是 HDFS)中。Pravega 流式存储通过数据写入可持久化的分层存储保证持久性,用户能够永久可靠地保存流数据。
有序性意味着读客户端将按照写入的顺序处理数据,Kafka 保证了消费者组内部是有序的。对于 Pravega 这样的通过路由键 (routing key) 来实现分区的系统而言,有序仅对具有相同键的数据有意义。例如拥有数百万传感器的物联网系统中,sensor-ID.metric 可能作为键,Pravega 的 Stream 能够保证读取该传感器的数据将按其写入的顺序进行。对于使用增量更新计算的聚合函数之类的应用,有序性是必不可少的。
一致性意味着即使面对组件故障,而且无论是从流的尾读还是追赶读,所有读客户端都会看到给定键的相同的有序数据视图。与持久性一样,Pravega 的一致性仅依靠存储系统的一致性是不够的。对 Pravega 而言,写客户端的写入操作是幂等的,而写入的数据对于 Pravega 而言也是不透明的(无法再次进行修改),我们以此实现了强一致性。我们基于 Pravega 的强一致性还抽象出了状态同步器的 API,用户可以在此之上构建轻量级的其它分布式系统的状态同步。
事务性写入对于跨链接的应用程序一次完全正确是必要的。不仅 Pravega 本身支持事务性的写入,更和 Apache Flink 的 Sink 集成,在 Flink 检查点之间建立事务,通过分布式两阶段提交协议支持端到端的事务和仅一次处理。
Pravega 系列文章计划
Pravega根据 Apache 2.0 许可证开源,我们欢迎对流式存储感兴趣的大家加入社区,与 Pravega 共同成长。下一篇系列文章将会从 Pravega 本身的架构出发,进一步介绍 Pravega 的特性细节。之后的文章会逐一介绍 Pravega 的各大特性、具体实现细节以及使用方法等,敬请大家期待。
本篇文章为系列文章第一篇,后面系列文章标题如下:
Pravega 架构和细节
Pravega 应用实例
Pravega 的动态伸缩特性
Pravega 的仅一次语义及事务支持
分布式一致性解决方案:状态同步器(StateSynchronizer)
与 Apache Flink 集成使用
参考:
作者介绍
滕昱: 就职于 DellEMC 非结构化数据存储部门(Unstructured Data Storage) 团队并担任软件开发总监。2007 年加入 DellEMC 以后一直专注于分布式存储领域。参加并领导了中国研发团队参与两代 DellEMC 对象存储产品的研发工作并取得商业上成功。从 2017 年开始,兼任 Streaming 存储和实时计算系统的设计开发工作。
周煜敏:复旦大学计算机专业研究生,从本科起就参与 DellEMC 分布式对象存储的实习工作。现参与 Flink 相关领域研发工作。
评论 1 条评论