• 沒有找到結果。

采用批处理架构和 Lambda 架构计数

第 4 章  对时间的处理

4.1  采用批处理架构和 Lambda 架构计数

尽管看起来简单,但是大规模的计数任务在实践中出人意料地困难。当然,

计数无处不在。针对联机分析处理多维数据集的聚合或其他操作,都可以 简单地归结为计数。图4-1 展示了如何采用传统的批处理架构实现计数 任务。

在该架构中,持续摄取数据的管道每小时创建一次文件。这些文件通常被 存储在HDFS 或 MapR-FS 等分布式文件系统中。像 Apache Flume 这样的 工具可以用于完成上述工作。由调度程序安排批处理作业(如MapReduce 作业)分析最近生成的一个文件(将文件中的事件按key 分组,计算每个 key 对应的事件数),然后输出计数结果。对于每个使用 Hadoop 的公司来 说,其集群都有多个类似的管道。

36 | 第 4 章

调度程序

时间

服务和存储

文件 1

文件 2

文件 3

作业 1

作业 2

作业 3

图4-1:用定期运行的批处理作业来实现应用程序的持续性。数据被持续地分割为 文件(如以一小时为单位);然后,批处理作业将文件作为输入,以此达到持续处 理数据的效果

这种架构完全可行,但是存在以下问题。

• 太多独立的部分。为了计算数据中的事件数,这种架构动用了太多系统。

每一个系统都有学习成本和管理成本,还可能存在bug。

• 对时间的处理方法不明确。假设需要改为每 30 分钟计数一次。这个变动 涉及工作流调度逻辑(而不是应用程序代码逻辑),从而使DevOps 问题 与业务需求混淆。

• 预警。假设除了每小时计数一次外,还需要尽可能早地收到计数预警(比 如在事件数超过10 时预警)。为了做到这一点,可以在定期运行的批 处理作业之外,引入Storm 来采集消息流(Kafka 或者 MapR Streams)。

Storm 实时提供近似的计数,批处理作业每小时提供准确的计数。但是 这样一来,就向架构增加了一个系统,以及与之相关的新编程模型。上 述架构叫作Lambda 架构,如图 4-2 所示。第 1 章有过简要的介绍。

对时间的处理 | 37

调度程序

流处理作业 时间

服务和存储

文件 1

文件 2

文件 3

作业 1

作业 2

作业 3

图4-2:Lambda 架构用定期运行的批处理作业来实现应用程序的持续性,并通过流 处理器获得预警。流处理器实时提供近似结果;批处理层最终会对近似结果予以纠正

• 乱序事件流。在现实世界中,大多数事件流都是乱序的,即事件的实际 发生顺序(事件数据在生成时被附上时间戳,如智能手机记录下用户登 录应用程序的时间)和数据中心所记录的顺序不一样。这意味着本属于 前一批的事件可能被错误地归入当前一批。批处理架构很难解决这个问 题,大部分人则选择忽视它。

• 批处理作业的界限不清晰。在该架构中,“每小时”的定义含糊不清,分 割时间点实际上取决于不同系统之间的交互。充其量也只能做到大约每 小时分割一次,而在分割时间点前后的事件既可能被归入前一批,也可 能被归入当前一批。将数据流以小时为单位进行分割,实际上是最简单 的方法。假设需要根据产生数据的时间段(如从用户登录到退出)生成 聚合结果,而不是简单地以小时为单位分割数据,则用如图4-1 和图 4-2 所示的架构无法直接满足需求。

38 | 第 4 章

(Kafka 或 MapR Streams)。消息传输系统为负责处理所有数据的流处理器(在本 例中是Flink)提供流数据,产生的结果既是实时的,也是正确的

DataStream<LogEvent> stream = env // 通过Kafka生成数据流

.addSource(new FlinkKafkaConsumer(...)) // 分组

.keyBy("country") // 将时间窗口设为60分钟 .timeWindow(Time.minutes(60)) // 针对每个时间窗口进行操作

.apply(new CountPerWindowFunction());

相關文件