1.4 实时流计算系统架构
1.2节介绍了实时流计算系统的多种使用场景。仔细分析这些系统的组成,我们不难发现,虽然使用场景多种多样、不尽相同,但这些系统都包含了5个部分:数据采集、数据传输、数据处理、数据存储和数据展示。事实上,也正是这5个部分构成了一般通用的实时流计算系统,如图1-5所示。
图1-5 实时流计算系统的组成部分
1.4.1 数据采集
数据采集是接收来自于各种数据源的数据,并将这些数据经过初步的提取和转换后,发送到数据传输系统的过程。为了使数据接收的性能最优,在设计数据采集方案时,必须充分考虑所接收数据的特点。例如:
❑ 数据接收的性能要求如何?
❑ 数据是逐条发送还是批次发送?
❑ 客户端到服务器的连接是长连接还是短连接?
❑ 最大并发连接数是多少?
表1-1列举了部分数据特点及相应处理方案。
表1-1 部分数据特点及相应处理方案
大多数情况下,数据采集服务器选择诸如Netty的非阻塞I/O方案会更加合适。数据被接收后,一般还需要对其做简单的处理,主要是一些字段提取和转化操作,最终将数据表示为统一的数据格式,如JSON、AVRO、Protobuf等。通常而言,数据组织的结构越简单越好,平坦的数据结构比嵌套式数据结构更好,嵌套浅的数据结构比嵌套深数据结构更好。最后,将整理好的数据序列化发往数据传输系统。
1.4.2 数据传输
数据传输是流数据在各个模块间流转的过程。数据传输系统的核心是消息中间件,常用的消息队列中间件有Apache Kafka、RabbitMQ等。数据传输系统就像人体的血管系统,承载着整个实时流计算系统的数传输工作。选择消息中间件时,需要考虑以下因素。
1)吞吐量:消息中间件每秒能够处理的消息数。消息中间件自身的吞吐量决定了实时流计算系统吞吐量的上限,所以选择消息中间件时,首先要确定消息中间件本身的吞吐量对业务没有明显的限制。
2)延迟:消息从发送端到消费端所消耗的时间。如同吞吐量一样,消息中间件自身的延迟决定了实时流计算系统延迟的下限。选择消息中间件时,需确定消息中间件本身延迟对业务没有明显限制。
3)高可用:消息中间件的一个或多个节点发生故障时,仍然能够持续提供正常服务。高可用消息中间件必须支持在转移故障并恢复服务后,客户端能自动重新连接并使用服务。千万不能让客户端进入僵死状态,否则即便消息中间件依然在提供服务,而上层的业务服务已然停止。
4)持久化:消息中间件中的消息写入日志或文件,在重启后消息不丢失。大部分业务场景下,支持持久化是一个可靠线上系统的必要条件。数据持久化从高可用角度看,还需要提供支持数据多副本存储功能。当一部分副本数据所在节点出现故障,或这部分副本数据本身被破坏时,可以通过剩余部分的副本数据恢复出来。
5)水平扩展:消息中间件的处理能力能够通过增加节点来提升。当业务量逐渐增加时,原先的消息中间件处理能力逐渐跟不上,这时需要增加新节点以提升消息中间件的处理能力。例如,Kafka可以通过增加Kafka节点和topic分区数的方式水平扩展处理能力。
1.4.3 数据处理
数据处理是实时流计算系统的核心。从数据传输系统读取数据流后,需要对数据流做处理。数据处理的目标可以分为4类:数据转化、指标统计、模式匹配以及模型学习和预测。
❑ 数据转化包括数据抽取、清洗、转换和加载,如常见的流式函数filter和map,分别用于完成数据抽取和转化的操作。
❑ 指标统计是在流数据上统计各种指标,如计数、求和、均值、标准差、极值、聚合、关联、直方图等。
❑ 模式匹配是在流数据上寻找预先设定的事件序列模式,我们常说的CEP(复杂事件处理)就属于模式匹配。
❑ 模型学习和预测是数据挖掘和机器学习在流数据上的扩展应用,基于流的模型学习算法可以实时动态地训练或更新模型参数,进而根据模型做出预测,更加准确地描述数据背后当时正在发生的事情。
我们通常使用DAG(Directed Acyclic Graph,有向无环图)来描述流计算过程。常见的开源流处理框架有Apache Storm、Apache Spark、Apache Flink、Apache Samza和Akka Streams等。在这些流处理框架中,都会使用DAG或类似的概念来表示流计算应用。
1.4.4 数据存储
数据存储方案的选型要充分考虑计算类型和查询目标。由于实时流数据的无限性和实时性特点,针对流处理的算法经常需要专门设计。
例如,针对“过去一天同一设备上登录的不同用户数”这种查询目标,在数据量较小时,传统关系型数据库(RBDM)和结构化查询语言(SQL)是不错选择。但当数据量变得很大后,基于关系型数据库的方案会变得越来越吃力,直到最后根本不可能在实时级别的时延内完成查询。
相同的查询目标,采用NoSQL数据库不仅能够做到实时查询,而且能获得更高的吞吐能力。相比传统SQL数据库,实时流计算中会更多地使用NoSQL数据库。越来越多的NoSQL数据库开始提供类似于SQL的查询语言,但查询语言不是数据库的本质所在,数据库的本质是底层的查询执行和数据存储。选择数据存储方案时,上层查询语言的通用性和易用性是重要的考虑因素。但更重要的是,所选数据库的查询和存储本身能够贴合所要进行查询的计算复杂度。
除了在实时流计算过程中需要使用数据库外,数据本身和计算结果通常需要保存起来,以做数据备份、离线报表或离线分析等。离线数据存储一般选择诸如HDFS或S3这样的分布式文件系统。特别是如今Hadoop已经非常成熟,构建在其上的查询和分析工具多种多样,如MapReduce、Hive和Spark等都是非常好的分析工具。这些工具统一在Hadoop生态体系内,为以后的工具选择留下很大的余地。
如果需要针对实时流计算结果构建实时点查询服务,即根据一个或多个键来查询一条特定的实时流计算结果记录,则可以选择NoSQL数据库并配置缓存的方案。
有时候实时流计算的结果使用UI呈现。很多UI会提供交互式查询体验,这就涉及Ad-Hoc查询。设计用于Ad-Hoc查询的存储方案时,一定要考虑UI可能的需求变化,而不能选择一个“僵硬”的数据存储方案,否则当未来UI需求变化、各种查询条件调整时,后端数据库的变更将是一个巨大而且痛苦的挑战。这种情况下,使用搜索引擎一类的存储方案(如ElasticSearch)会是一个明智的选择。
综合而言,在相对复杂的业务场景下,实时流计算只是系统中的一个环节。针对不同计算类型和查询目的,要合理选择相应的数据存储方案。更有甚者,很多时候必须将相同内容的数据根据不同的需求,同时存入多种不同功能的存储方案中。至少目前为止,还没有一种称之为“银弹”的数据库。在本书第9章中,我们将详细讨论各种数据存储方案。
1.4.5 数据展示
数据展示是将数据呈现给最终用户的过程。数据呈现的形式可以是API,也可以是UI。API的方式通常以REST服务形式提供。大多数UI是以We.UI的方式实现的,在移动终端大行其道的今天,诸如手机的客户端应用程序也是常用的数据呈现方式。对于We.UI而言,基于Web的数据展示方式有很多优点。一方面,Web服务实现和部署都非常简单,只需提供Web服务器就可以在浏览器中进行访问了。另一方面,各种丰富的前端框架和数据可视化框架为开发提供了更多的便利和选择,如前端常用的框架有React、Vue、Angular等,常用的数据可视化框架有ECharts、D3.js等。
数据可视化是数据展示的核心所在,数据可视化的内容也很丰富、精彩。本书会讨论如何为数据展示选择最合适的数据存储方案。但因为数据可视化部分更加偏向于前端(包括JS、CSS、HTML和UI设计等),这与实时流计算的主体并无太强关联,所以除了部分涉及针对数据展示该如何设计数据存储方案的内容以外,本书不会再用专门的章节讨论数据展示的有关内容。感兴趣的读者可以自行参考前端和数据可视化的有关资料和书籍。