原文:https://flink.apache.org/flink-architecture.html
Apache Flink是一个可对无界和有界数据流进行有状态计算的分布式处理框架.
Flink能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算.
下面,我们开始介绍Flink架构中的重要方面.
处理无界和有界数据
所有类型的数据都可作为事件流来产生. 信用卡交易,传感器测量,机器日志,或网站/移动应用程序上的用户交互,所有这些数据都能以流形式生成。
数据可作为无界或有界流处理:
无界流(Unbounded streams):有开始无结束. 无界流是持续的,不会终止,因此必须对其连续处理(即必须在摄取之后立即处理事件)。无法等待所有输入数据到达,因为输入是无界的,并且在任何时间点都不会完成传输。 处理无界数据通常要求必须以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果完整性。
有界流(Bounded streams):有开始有结束. 可以在执行计算之前通过摄取所有数据来处理有界流。 处理有界流不需要有序摄取,因为始终都可对有界数据集进行排序。 有界流的处理也称为批处理。
![](https://img.haomeiwen.com/i15627055/b04812d495d43aef.png)
Apache Flink擅长处理无界和有界数据集。 精确控制时间和状态能让Flink运行时能在无界流上运行任何类型的应用程序。 有界流通过算法和数据结构进行内部处理,这些算法和数据结构专门针对固定大小的数据集而设计,因而性能更为出色。
Deploy Applications Anywhere
Apache Flink是一个分布式系统,它需要计算资源才能执行应用程序。 Flink不仅能与常见的集群资源管理器(如Hadoop YARN,Apache Mesos和Kubernetes)集成,而且还可为作为独立集群运行。
Flink能很好地与前面列出的每个资源管理器一起工作。 这是通过特定于资源管理器的部署模式实现的,这些模式允许Flink以其惯用方式与每个资源管理器进行交互。
部署Flink应用程序时,Flink会根据应用程序配置的并行度(parallelism)自动识别所需资源,并从资源管理器请求这些资源。 如果发生故障,Flink会通过请求新资源来替换发生故障的容器。 提交或控制应用程序的所有通信都通过REST调用进行,这简化了Flink在许多环境中的集成。
Run Applications at any Scale
Flink能在任意规模的集群中运行有状态流应用程序.借助于Flink,应用程序能轻松地并行化为数千个可在集群中同时运行的任务. 因此,应用程序可以利用几乎无限量的CPU,主内存,磁盘和网络IO.
而且,Flink还可轻松地保存非常大的应用程序状态。在保证exactly-once 状态一致的前提下,Flink的异步和增量检查点算法还能最大化地降低对延迟的影响.
下面是在用户生产环境中运行Flink应用程序时报告的可扩展性数字,例如
应用程序每天处理数万亿个事件
应用程序维护多个TB级数据状态
应用程序可运行在数千核之上
Leverage In-Memory Performance
有状态Flink应用程序对本地状态访问进行了优化。 任务状态始终保留在内存中,如果状态超过可用内存大小,则会将状态保存在能高效访问的磁盘数据结构中。
因此,任务通过访问本地(通常是内存)状态来执行所有计算,从而产生非常低的处理延迟。
Flink通过定期异步地将本地状态检查点到持久存储来保证在出现故障时的exactly-once状态一致性。
![](https://img.haomeiwen.com/i15627055/b51f312463b06935.png)
Apache Flink是一个可对无界和有界数据流进行有状态计算的框架。
Flink在不同抽象层次提供多个API,并为常见用例提供专用库。
在这里,我们将介绍Flink易于使用和富有表现力的API和库。
Building Blocks for Streaming Applications
可以由流处理框架构建和执行的应用程序类型是通过框架controls streams,state和time的程度来定义的。
下面我们将介绍流处理应用程序中的这些构建块,并说明Flink对它们的处理方法。
Streams
显然,streams是流处理的基础.但,流可以具有不同特征,这些特征会影响流的处理方式.
Flink是一个多功能的处理框架,可以处理任何形式的流。
有界(Bounded)和无界(unbounded)流: 流可以是无界的,也可以是有界的(即:固定大小的数据集). Flink具有处理无界流的复杂功能,但也提供专门的算子来有效地处理有界流。
实时(Real-time)和记录(recorded)流: 所有数据都可当成流. 有两种处理数据的方式.
一种是实时处理,另一种是先将流存储到存储系统(例如,文件系统或对象存储),然后后期再处理.
Flink 应用程序能处理这两种记录或实时流.
State
每个特殊的流应用程序都是有状态的,也就是说,只有在单个事件上应用转换的应用程序才不需要状态. 运行基本业务逻辑的任何应用程序都需要记住事件或中间结果,以便能在以后的某个时间点(例如,当接收到下一个事件或在特定的时间段之后)访问它们。
![](https://img.haomeiwen.com/i15627055/37170bc1b60a5221.png)
应用程序是Flink中的头等公民. 通过查看Flink在状态处理上下文中提供的所有特性,即可证实这一点。
Multiple State Primitives: Flink为不同的数据结构提供状态原语,例如原子值(atomic values),列表(lists)或映射(maps)。
开发人员可以根据函数的访问模式选择最有效的状态原语。
Pluggable State Backends: 应用程序状态由可插拔状态后端管理和检查。
Flink具有不同的状态后端,可以在内存或RocksDB中存储状态,RocksDB是一种高效的嵌入式磁盘数据存储。 也可以插入自定义状态后端。
Exactly-once state consistency:Flink的检查点和恢复算法可确保在发生故障时应用程序状态的一致性。 因此,故障是透明处理的,不会影响应用程序的正确性。
Very Large State: 由于其异步和增量检查点算法,Flink能够维持TB字节的应用程序状态。
Scalable Applications: Flink通过将状态重新分配给更多或更少的workers来支持有状态应用程序的扩展。
Time
时间是流应用程序的另一个重要组成部分。 大多数事件流都具有固有的时间语义,因为每个事件都是在特定时间点发生的。
此外,许多常见的流计算都是基于时间的,例如窗口聚合,会话化,模式检测和基于时间的连接。
流处理的一个重要方面是应用程序如何测量时间,即事件时间和处理时间的差异。Flink提供了一组丰富的与时间相关的功能。
Event-time Mode: 处理具有事件时间语义的流应用程序会基于事件的时间戳来计算结果。 因此,无论是否处理记录还是实时事件,事件时间处理都能保证准确一致结果。
Watermark Support: Flink使用水印来推断事件时间应用中的时间。 水印也是一种灵活的机制,可以权衡结果的延迟和完整性。
Late Data Handling: 当以带有水印的事件时间模式(event-time mode)处理流时,可能会在所有关联事件到达之前完成计算。这类事件称为延迟事件。
Flink具有多个选项来处理延迟事件,例如通过重新路由输出方数据来更新之前完成的计算结果。
Processing-time Mode: 除了event-time mode外, Flink还支持processing-time 语义,该处理时间语义执行由处理机器的挂钟时间(wall-clock time )来触发计算。
processing-time mode适用于具有严格低延迟要求的某些应用,这些要求可以容忍近似结果。
Layered APIs
Flink提供三层API。 每个API在简洁性和表达性之间提供不同的权衡,并针对不同的用例。
![](https://img.haomeiwen.com/i15627055/903ce1fa8157b062.png)
我们简要介绍每个API,讨论其应用程序,并展示代码示例。
ProcessFunctions
ProcessFunctions 是Flink中最具表现力的函数接口.
Flink提供ProcessFunction来处理来自窗口分组中一个或两个输入流/事件的单个事件。 ProcessFunctions提供对时间和状态的细粒度控制。
ProcessFunction可以任意修改其状态,并可注册一个可在后期触发回调函数的定时器。
因此,ProcessFunctions可在许多有状态事件驱动应用程序中实现复杂的事件业务逻辑。
下面展示了一个KeyedProcessFunction,它针对KeyedStream进行操作并匹配START和END事件。
收到START事件时,该函数会记住其状态的时间戳,并注册一个四小时之后触发的计时器。
如果在计时器触发之前收到END事件,则该函数会计算END和START事件之间的持续时间,清除状态并返回该值。 否则,计时器只会清除状态。
/** * 匹配START和END事件,并计算两个元素时间戳之间的差异。
* 第一个String字段是key属性,第二个String属性用于标记START和END事件。 */
public static class StartEndDuration
extends KeyedProcessFunction<String, Tuple2<String, String>, Tuple2<String, Long>> {
private ValueState<Long> startTime;
@Override
public void open(Configuration conf) {
// 获得state句柄
startTime = getRuntimeContext().getState(new ValueStateDescriptor<Long>("startTime", Long.class));
}
/** 每当处理事件时调用 **/
@Override
public void processElement(Tuple2<String, String> in, Context ctx, Collector<Tuple2<String, Long>> out)
throws Exception {
switch (in.f1) {
case "START": // 收到start事件则设置开始时间.
startTime.update(ctx.timestamp());
// 注册timer
ctx.timerService().registerEventTimeTimer(ctx.timestamp() + 4 * 60 * 60 * 1000);
break;
case "END": // 发射事件持续时间
Long sTime = startTime.value();
if (sTime != null) {
out.collect(Tuple2.of(in.f0, ctx.timestamp() - sTime));
// 清除状态
startTime.clear();
}
default: // do nothing
}
}
/** 触发timer时调用 **/
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<Tuple2<String, Long>> out) {
// 事件超时则清除状态 startTime.clear();
}
}
该示例展示了KeyedProcessFunction的表达能力,但同时也说明它是一个相当冗长的接口。
DataStream API
DataStream API提供了常用流处理操作原语,例如:窗口,一次记录转换,以及通过查询外部存储来丰富事件等操作.
DataStream API适用于Java和Scala,并且它是基于函数的,例如map(), reduce(), and aggregate(). 可以通过扩展接口或Java/Scala lambda函数来定义函数。
下面的示例用于统计每个会话的点击次数.
// 网站点击流
DataStream<Click> clicks = null;
DataStream<Tuple2<String, Long>> result = clicks.map(
// 实现MapFunction接口来自定义函数.
new MapFunction<Click, Tuple2<String, Long>>() {
@Override
public Tuple2<String, Long> map(Click click) {
return Tuple2.of(click.userId, 1L);
}
})
.keyBy(0) // 以userId (field 0)作为key
.window(EventTimeSessionWindows.withGap(Time.minutes(30L)))// 以30分钟间隙作为会话窗口
.reduce((a, b) -> Tuple2.of(a.f0, a.f1 + b.f1));// 计算每个会话的点击数,以lambda来定义函数。
SQL & Table API
Flink提供两个关系API-Table API和SQL. 这两个API是批处理和流处理的统一API,即,在无界的实时流或有界的记录流上都可使用相同的语义执行查询,并产生一致结果。Table API 和 SQL 借助Apache Calcite来执行解析,验证,以及查询优化. 它们能与DataStream/DataSet API无缝集成,并支持用户自定义的标量,聚合和表值函数。
Flink关系API旨在简化数据分析,数据流水线和ETL应用程序的定义。
下面的示例展示的是会话点击流SQL查询,并计算每个会话的点击次数。 这与DataStream API示例中的作用相同。
SELECT userId, COUNT(*)FROM clicksGROUP BY SESSION(clicktime, INTERVAL '30' MINUTE), userId
Libraries
Flink提供了可处理常见数据处理操作的库.这些库嵌在了API中,而非完全独立. 因此,他们可以从API的所有功能中受益,并与其他库集成。
Complex Event Processing (CEP): 模式检测是事件流处理中一种非常常见的用例。 Flink的CEP库提供了一个API来指定事件模式(可联想一下正则表达式或状态机)。 CEP库与Flink的DataStream API集成,以便在DataStream上评估模式。 CEP库的应用包括网络入侵检测,业务流程监控和欺诈检测。
DataSet API: DataSet API是Flink中执行批处理的核心API。 DataSet API的原语包括map,reduce,(outer) join,co-group和iterate。 所有操作都由算法和数据结构组成,这些算法和数据结构会对内存中的序列化数据进行操作,并在数据大小超过内存预算时溢出到磁盘。 Flink的DataSet API的数据处理算法受到传统数据库运算符的启发,例如 hybrid hash-join或 external merge-sort。
Gelly: Gelly是一个可扩展的图形处理和分析库。 Gelly基于DataSet API实现,并能与其集成。 因此,它能从可扩展且强大的片子中受益。Gelly有自己的内置算法 ,例如标签传播,三角形枚举和页面排序。但为了实现定制化,它也提供了一种Graph API ,借助该API我们可以简化自定义图算法的实现。
Apache Flink是一个用于对无界和有界数据流进行有状态计算的框架。由于许多流应用程序设计为连续运行(停机时间非常少),因此流处理器必须提供出色的故障恢复,以及应用程序运行时监视和维护工具。前面,我们重点介绍的是流处理操作。在这里,我们将介绍Flink的故障恢复机制,以及应用程序管理和监控等功能。
全天候运行应用程序
在分布式系统中,机器和进程故障无处不在. 像Flink这样的分布式流处理器必须要从故障中恢复,以便能够24/7全天候运行流应用程序.
显然,这不仅意味着在排除故障后能重启应用程序,而且还要确保其内部状态保持一致,以便应用程序可以继续处理,就像从未发生过故障一样。
Flink提供了多种功能来确保应用程序保持运行时一致:
Consistent Checkpoints: Flink的恢复机制基于应用程序状态的一致检查点.如果发生了故障,应用程序重启时会根据最后一个检查点来加载状态.结合可重置流,该特性能保证exactly-once state consistency.
Efficient Checkpoints: 如果应用程序保存了TB级别的状态数据,那么在这种情况下检查应用程序的状态将变得异常昂贵. Flink可执行异步和增量检查点,这样检查点对应用程序的延迟影响降低到最小水平。
End-to-End Exactly-Once: Flink提供特定存储系统的事务接收器(sinks),可确保数据只写一次,即便出现故障也是如此。
Integration with Cluster Managers: Flink能与各种集群资源管理器集成,例如:Hadoop YARN, Mesos,或Kubernetes. 当某个进程失败时,它会自动启动一个新进程来接管其工作。
High-Availability Setup: Flink具有高可用性特性,它能消除所有单点故障。 HA模式基于Apache ZooKeeper,这是一种经过验证的可靠分布式协调服务。
更新,迁移,暂停和恢复应用程序
对于那些涉及核心业务的流应用程序,我们必须对其细心维护.但更新有状态流应用程序并非易事.
通常,我们不能像普通应用程序一样简单地停止应用程序并重启到固定版本或改进版本来更新流应用程序,因为我们无法承受丢失应用程序状态的损失。
Flink的savepoint是一独特而强大的功能,它可以解决更新有状态应用程序时所面临的一系列问题。 savepoint是应用程序状态的一致快照,因此它与检查点(checkpoints)非常相似.
但是,与检查点不同的是savepoint需要手动触发,并在应用程序停止时需手动删除(不会自动删除)。
savepoint可用于启动与状态相兼容的应用程序,并初始化其状态。 savepoint启用了以下功能:
Application Evolution: savepoint可用来进化应用程序. 应用程序的固定版本或改进版本可通过应用程序之前版本保存的savepoint来重新启动. 也可以从较早的时间点(假设存在这样的savepoint)启动应用程序,以便修复缺陷版本所产生的错误结果。
Cluster Migration: 使用savepoint,可以将应用程序迁移(或克隆)到不同的集群。
Flink Version Updates: 可使用savepoint来将应用程序迁移到新的Flink版本上运行.
Application Scaling: savepoint可用来加大或降低应用程序的并发度。
A/B Tests and What-If Scenarios: 可通过同一savepoint来启动所有版本的应用程序,以便比较不同版本间的性能或质量差异。
Pause and Resume: 可通过savepoint来暂停或停止应用程序. 然后,在之后的任意时间点,都可通过该savepoint来恢复应用程序.
Archiving: 可以存档savepoint,以便能够将应用程序的状态重置为较早的时间点。
监控和控制应用程序
与任何其他服务一样,需要对连续运行的流应用程序进行监控,并将这些信息集成到日志服务中。
监控有助于预测问题并提前做出反应。 日志则有助于排查问题的根本原因。 最后,控制应用程序运行的管理界面也是一个重要功能。Flink能很好地与许多常见的日志和监控服务集成,并且它还提供了REST API来控制应用程序,以及查询相关信息。
Web UI: Flink提供了一个web UI来检查,监视和调试正在运行的应用程序。此外,还可通过该web UI来提交或取消作业.
Logging: Flink实现了当下最为流行的slf4j logging接口,并能与log4j或logback这样的日志框架无缝集成.
Metrics: Flink提供了非常复杂的指标系统,这些指标系统会收集系统层面和用户层面定义的指标数据. 指标可通过多种方式获取,例如:JMX, Ganglia, Graphite, Prometheus, StatsD, Datadog, 和Slf4j.
REST API: Flink暴露了一个REST API来执行某些操作,比如:提交新应用程序,获取正在运行应用程序的savepoing,或取消正在运行的应用程序. 此外,该API还暴露了与应用程序相关的元数据和运行/完成指标。
网友评论