流处理平台与 Apache Flink 实战解析
1. 流处理平台概述
近年来,流处理平台如雨后春笋般涌现,有多种开源、专有以及云服务提供商提供的解决方案,它们各有优缺点。不过,不同平台的底层架构和机制较为相似。
流处理平台的数据通过各种数据源提供,常见的数据源包括队列(如 Kafka 主题)或分布式存储系统中的文件(如 S3)。流处理节点从数据源摄取数据对象,并执行转换、聚合和特定于应用程序的业务逻辑。节点以有向无环图(DAG)的形式组织,来自源的数据对象作为流进行处理,数据流是单个数据对象的无界序列。由于数据对象在处理节点之间传递或流动,流处理应用程序也被称为数据流系统。
流处理系统使处理节点能够将一个节点的输入流转换为一个或多个下游节点处理的新流。例如,运输应用程序可以从公交位置更改事件流中每 30 秒生成一个当前公交位置的新流。
流处理应用程序主要有两种类型:
-
无状态流处理应用程序
:简单地处理和转换流中的单个事件,不需要每个事件的任何上下文或状态。例如,输入可穿戴设备的最新数据更新流,并将单个数据对象转换为代表用户最新步数、心率和每小时活动数据的其他对象,结果写入数据库或队列等数据接收器,用于下游异步处理,如计算静息心率、燃烧的卡路里等。
-
有状态流处理应用程序
:需要维护在流中处理单个数据对象时持续存在的状态。例如,运输监控应用程序必须了解所有正在移动的公交车,并维护代表过去 30 秒内位置更新的状态;欺诈检测应用程序必须维护代表识别可疑交易所需的当前模型参数的状态;零售商店流处理应用程序必须维护代表过去一小时内每个商品销售数量的信息,以识别高需求商品。
此外,流处理平台需要具备使应用程序能够扩展其处理能力并对故障具有弹性的功能。这通常通过在计算资源集群上执行多个处理节点实例,并实现状态检查点机制以支持故障后恢复来实现,具体实现方式因平台而异。
2. Apache Storm 示例
以 Apache Storm 为例,以下代码创建了一个具有单个数据源和两个处理节点的流处理应用程序(在 Storm 中称为拓扑),它们以简单的管道形式排列:
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("purchasesSpout", new PurchasesSpout());
builder.setBolt("totalsBolt", new PurchaseTotals(), numTotalsBolts)
.fieldsGrouping("purchasesSpout", new Fields("itemKey"));
builder.setBolt("topSellersBolt", new TopSellers())
.globalGrouping("totalsBolt");
其工作原理如下:
-
PurchasesSpout
对象从数据源将购买记录作为流发出,在 Storm 中,spout 将流处理应用程序连接到数据源(如队列)。
- 购买流从 spout 传递到处理节点对象(称为 bolt),即
PurchaseTotals
对象,它维护所有商品的购买总数。由
numTotalsBolts
参数定义的多个 bolt 实例由 Storm 作为独立线程执行。
fieldsGrouping
确保具有相同
itemKey
值的购买记录始终从 spout 发送到同一个 bolt 实例,以便每个键的总数由单个 bolt 管理。
-
PurchaseTotals
bolt 将更改后的总购买流发送到
TopSellers
bolt,该 bolt 创建流中畅销商品的排行榜。
globalGrouping
将所有
PurchaseTotals
实例的输出路由到单个
TopSellers
bolt 实例。
Apache Storm 是一个强大且可扩展的流处理平台,但其 API 相对简单,需要应用程序设计人员明确定义拓扑。接下来,我们将重点介绍更现代的 Apache Flink。
3. Apache Flink 简介
Apache Flink 于 2014 年基于欧盟 Stratosphere 项目的原始研究而诞生。其核心是一个分布式流处理系统,旨在实现高吞吐量和低延迟。Flink 提供了一系列用于过滤、聚合、映射和连接来自数据源的数据流的操作。与显式定义的 Apache Storm 拓扑不同,Flink 程序被编译并自动转换为可以部署在集群计算环境中的数据流程序。
Flink 提供了多种不同的 API,下面将简要介绍 DataStream API,同时它还支持基于关系概念的 Table 和 SQL API,可以构建对具有定义模式的表中累积的数据流执行 SQL SELECT 查询子集的流处理应用程序。
4. DataStream API 实战
Flink DataStream API 为 Java 和 Scala 系统提供流处理功能,可以利用丰富的流处理操作来拆分、过滤、聚合和转换事件流,并使用有界时间窗口对流中的事件批次进行定期处理。
在 Flink 中,数据流是类型化事件流的逻辑表示,在 Java 中为
DataStream<T>
。数据流应用程序的每个阶段对事件应用函数并生成类型化输出事件流。Flink 可以通过复制处理管道中的函数并将事件分发到不同的副本并行处理流。
使用 Flink 的基本步骤如下:
1.
创建执行环境
:
final StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
执行环境可以是本地的(在单个 JVM 中执行)或远程的(在计算集群上执行),Flink 环境对象可以指定各种作业执行参数,控制应用程序在集群上的可扩展性和容错性。
2.
指定数据源
:
Flink 支持多种原生数据源(如文件),并具有与各种外部技术的连接器。以下示例展示了如何使用 Flink Kafka 连接器从 Kafka 主题摄取数据:
KafkaSource<LiftRide> source = KafkaSource.<LiftRide>builder()
.setBootstrapServers(brokerList)
.setTopics("resort-topic")
.setGroupId("liftConsumers")
.setStartingOffsets(OffsetsInitializer.earliest())
.setValueOnlyDeserializer(new LiftRideSchema())
.build();
DataStream<LiftRide> liftRideStream =
env.fromSource(source, WatermarkStrategy.noWatermarks(),
"Resort Lifts");
在这个例子中,Flink 从主题的开始处读取数据(
OffsetsInitializer.earliest()
),并使用 Kafka 事件时间戳作为消息时间(
WatermarkStrategy.noWatermarks()
)。
3.
指定转换操作
:
以下示例展示了如何计算度假村中每个滑雪缆车的单个乘坐次数:
DataStream<Tuple2<String, Integer>> liftCounts =
liftRideStream
.map(i -> Tuple2.of(i.getLiftID(), 1))
.returns(Types.TUPLE(Types.STRING, Types.INT))
.keyBy(value -> value.f0)
.sum(1)
.window(SlidingProcessingTimeWindows.of(Time.minutes(10),
Time.minutes(5)));
代码的基本工作原理如下:
- Flink 从源中提取每个
LiftRide
事件并将其传递给
map()
函数。
-
map()
生成一个
Tuple2
类型的新对象,包含两个类型化元素:
STRING
类型的
liftID
和代表单个乘坐次数的
INT
类型值 1。
-
keyBy()
使用
liftID
(元组中的字段 0)作为键对
map
输出进行分区,
sum(1)
运算符为每个键保留单个乘坐次数的总数(元组中的字段 1)。
- 滑动窗口定义了 Flink 生成结果的时间。Flink 维护一个包含过去 10 分钟内处理的所有事件的窗口(窗口大小),然后每 5 分钟生成一组
Tuple2<String, Integer>
类型的结果,代表基于过去 10 分钟内处理的事件每个滑雪缆车的滑雪者乘坐次数(窗口滑动)。
一般来说,窗口操作定义了有限事件集的边界并对该事件集执行操作。滑动窗口对于执行加权平均等计算非常有用,而翻滚窗口定义了不同的窗口边界,每个事件只能属于单个窗口。
4.
添加数据接收器
:
liftRideStream.addSink( … ) // 参数省略
-
启动流处理
:
Flink 程序是惰性执行的,直到调用以下代码才会开始处理:
env.execute();
5. Flink 的可扩展性
Flink 程序被转换为逻辑 DAG,数据流通过代码中定义的转换从源移动到接收器,这些转换在 DAG 中表示为节点。在部署时,Flink 将逻辑图映射到执行系统的物理资源,这些物理资源可以从本地节点到运行在数百个计算节点上的大型集群。
可以通过以下两种主要方式影响 Flink 将逻辑 DAG 部署到物理资源的方式:
-
指定转换的并行度
:在代码中,可以通过以下两种方式告知 Flink 运行时要执行的运算符的并发实例数量。例如,指定
.sum
转换的 10 个并行实例:
.sum(1).setParallelism(10);
这将导致 Flink 创建一个执行 DAG,
map()
函数发出的键值对通过哈希分区分布在 10 个
.sum()
运算符实例上。
-
配置执行环境的并发度
:可以使用执行环境对象为程序中的所有运算符、数据源和数据接收器指定默认并行度。例如,将默认并行度级别设置为 5:
final StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(5);
默认并行度级别会被显式定义并行度的运算符覆盖。
当提交应用程序时,Flink 将逻辑 DAG 映射到目标集群中可用的物理节点。集群中的每个计算节点运行一个 Flink 任务管理器,负责执行流处理系统的组件。每个任务管理器是一个 JVM,默认情况下可以运行系统中定义的一个并行任务,称为任务槽。可以通过在
flink-conf.yaml
文件中定义
taskmanager.numberOfTaskSlots
配置参数来指定 Flink 可以部署到任务管理器的任务数量,默认值为 1。一种常见的策略是为每个任务管理器节点分配与可用 CPU 核心数量相同的槽数。
Flink 实现了一种复杂的转换算法,将逻辑 DAG 映射到可用的物理资源,包括称为运算符链的优化,以将运算符放置在单个任务槽中,以最小化数据通信成本。一般来说,不需要深入了解所采用算法的细节。
6. Flink 的数据安全
对于任何流处理系统,处理故障都是需要考虑的问题。如果部署的流处理应用程序的一部分由于节点崩溃、网络故障或应用程序异常而失败,内存中保存的任何状态都会丢失。例如,在滑雪缆车乘坐次数的示例中,10 分钟窗口的乘坐次数计数保存在内存中,如果任务管理器失败,它管理的本地状态将丢失。
Flink 支持数据安全需要两种机制:持久状态存储和定期对完整流调用检查点。
-
配置持久状态存储
:需要将有状态运算符配置为定期将其状态作为键值对持久化。默认情况下,Flink 支持 RocksDB 存储后端,可以在
flink-conf.yaml
文件中进行如下配置:
state.backend: rocksdb
state.checkpoints.dir: file:///checkpt-mystream/
持久存储使得在流处理失败时可以从快照恢复状态。在流处理应用程序中,挑战是确保所有运算符具有一致的检查点,即所有运算符的快照基于处理来自流源的完全相同的输入事件。
-
使用流屏障实现一致检查点
:Flink 使用流屏障的概念确保快照一致。Flink 作业管理器定期将称为屏障的事件注入源流,这些屏障与源流中的事件严格按顺序流动。当有状态运算符在其所有输入流上接收到屏障事件时,它将其本地状态写入持久存储,然后在其输出上回显屏障,以便流处理中的下游运算符也可以持久化其状态。
屏障包含一个标识符,代表其在源输入流中的位置。例如,如果输入源是 Kafka 主题,屏障标识符代表流中屏障之前事件的偏移量 N,这确保所有检查点状态基于处理来自源的直到并包括偏移量 N 的所有事件。一旦屏障传递到流接收器的所有输入,检查点就标记为完成,这成为应用程序在发生任何故障时可以恢复的最新状态。
恢复涉及在分布式数据流中发生故障后恢复应用程序的状态。Flink 通过首先停止并重新部署整个应用程序,然后通知每个有状态运算符从最新完成的快照恢复其状态,最后通知流消费者从数据源中的位置 N + 1 恢复处理来实现这一点。
检查点有效地使 Flink 应用程序具有容错能力,但代价是定期进行状态检查点和恢复的成本。如果管理的状态较小,这对吞吐量的影响最小,但如果运算符维护较大的状态空间,频繁的检查点可能会显著影响流吞吐量。可以通过各种配置参数控制检查点的触发时间,例如设置两次检查点之间必须经过的最小时间,默认情况下,Flink 应用程序的检查点功能是禁用的。
7. 总结
流处理系统在许多应用领域中能够产生相关且及时的结果,具有很大的吸引力。可以实时转换、聚合和分析传入数据,应用程序可以基于时间窗口或消息量对有限的数据批次进行分析,从而识别数据趋势并根据最新数据窗口中的值计算指标。
有许多流处理平台可用于构建容错、可扩展的应用程序。可扩展性通过将逻辑数据流应用程序架构转换为物理等效架构来实现,该架构在集群计算资源中分布和连接系统中的处理节点。容错机制持久化处理节点状态并跟踪整个数据流应用程序中哪些消息已成功处理,当发生故障时,流可以从第一个未处理的消息重新启动。
流处理平台与 Apache Flink 实战解析
8. 流处理平台应用优势与挑战
流处理平台在当今数据驱动的时代展现出诸多显著优势,但也面临一些挑战,以下是详细分析:
|优势|挑战|
| ---- | ---- |
|实时性:能够实时处理和分析数据,及时响应业务需求。例如运输监控应用可实时获取公交位置信息。|故障处理:部分组件故障可能导致内存状态丢失,需有效机制保障数据安全。如任务管理器故障会丢失本地管理状态。|
|可扩展性:可通过集群计算资源扩展处理能力,满足业务增长。如Flink可将逻辑DAG映射到不同规模物理资源。|状态管理:有状态流处理应用需维护状态,大规模状态管理增加复杂度。如零售应用统计商品销售数量需准确维护状态。|
|灵活性:提供多种API和操作,可根据需求定制流处理应用。如Flink的DataStream、Table和SQL API。|性能开销:定期状态检查点和恢复操作会影响吞吐量,尤其在大状态空间时。如Flink频繁检查点会降低流处理速度。|
9. 不同流处理平台对比
为了更好地选择适合的流处理平台,下面对 Apache Storm 和 Apache Flink 进行对比:
|对比项|Apache Storm|Apache Flink|
| ---- | ---- | ---- |
|拓扑定义|需应用程序设计人员明确定义拓扑,对设计要求较高。|程序编译后自动转换为数据流程序,部署更便捷。|
|API复杂度|API相对简单,但功能实现依赖拓扑定义。|提供多种API,功能丰富且支持函数式编程。|
|性能特点|强大且可扩展,但在处理复杂任务时可能需更多配置。|旨在实现高吞吐量和低延迟,性能表现出色。|
|容错机制|有一定容错能力,但实现方式较依赖应用设计。|通过持久状态存储和流屏障实现高效容错。|
10. 流处理平台操作流程总结
流处理平台的操作通常遵循以下流程:
graph LR
A[数据源] --> B[流处理节点]
B --> C[转换操作]
C --> D[聚合分析]
D --> E[数据接收器]
F[故障处理] --> B
G[状态管理] --> B
- 数据源 :提供数据,如 Kafka 主题、分布式存储系统文件。
- 流处理节点 :摄取数据并执行基本处理。
- 转换操作 :对数据进行过滤、映射等转换。
- 聚合分析 :进行统计、计算等操作。
- 数据接收器 :存储处理结果,如数据库、队列。
- 故障处理 :通过检查点等机制恢复状态。
- 状态管理 :维护有状态应用的状态信息。
11. Flink 应用场景拓展
除了前面提到的运输监控、欺诈检测和零售应用,Flink 还可应用于以下场景:
-
金融交易监控
:实时监测金融交易,识别异常交易行为,保障资金安全。例如对股票交易、银行转账等进行实时分析。
-
物联网数据处理
:处理大量物联网设备产生的数据,如传感器数据、智能电表数据等,实现设备状态监测和预测性维护。
-
社交媒体分析
:分析社交媒体上的用户行为和内容,如话题热度、用户情感分析等,为企业营销和舆情管理提供支持。
12. 代码优化建议
在使用 Flink DataStream API 进行开发时,可参考以下优化建议:
-
合理设置并行度
:根据集群资源和数据量,合理设置运算符的并行度,避免资源浪费或不足。例如在处理大规模数据时,适当增加并行实例数量。
.sum(1).setParallelism(20); // 根据实际情况调整并行度
- 优化窗口操作 :选择合适的窗口类型和大小,提高计算效率。如对于实时性要求高的场景,可使用较小的窗口大小。
.window(SlidingProcessingTimeWindows.of(Time.minutes(5), Time.minutes(2))); // 调整窗口大小和滑动间隔
- 减少数据传输 :通过运算符链等优化手段,减少数据在节点间的传输,降低通信成本。
13. 未来发展趋势
随着数据量的不断增长和业务需求的日益复杂,流处理平台将朝着以下方向发展:
-
智能化处理
:引入机器学习和人工智能技术,实现更智能的数据分析和决策。例如自动识别数据模式和异常情况。
-
云原生支持
:更好地与云服务集成,利用云的弹性和扩展性,降低成本。如在云环境中快速部署和扩展流处理应用。
-
统一编程模型
:提供更统一的编程模型,简化开发流程,提高开发效率。让开发者可以更方便地使用不同的 API 进行开发。
14. 总结与展望
流处理平台在实时数据处理领域发挥着至关重要的作用,Apache Flink 作为其中的佼佼者,凭借其高吞吐量、低延迟、丰富的 API 和强大的容错能力,为开发者提供了优秀的解决方案。通过合理运用 Flink 的各种功能和优化技巧,可以构建出高效、稳定的流处理应用。
未来,随着技术的不断进步,流处理平台将不断创新和完善,为更多领域带来实时数据处理的价值。开发者应持续关注流处理技术的发展趋势,不断提升自己的技能,以应对日益复杂的业务需求。同时,在实际应用中,要根据具体场景选择合适的流处理平台和优化策略,确保系统的性能和可靠性。
超级会员免费看
1169

被折叠的 条评论
为什么被折叠?



