Apache Flink编程模型

原创
2017/05/23 11:30
阅读数 370

层次模型

    Flink编程模型提供了4个抽象层次,它们分别是:

  • 最底层:提供了有状态流,通过Process Function被集成进DataStream API和DataSet API。用户可自由地处理多个stream中的事件维护一致性状态已达到高容错性。另外用户还可以注册event time回调和processing time回调来实现负载的逻辑计算
  • Core API层:实际上,大多数应用不会使用最底层API,相反地,它们会直接使用Core API层,即DataStream API和DataSet API——它们是数据处理的基础构件(building block),实现了各种形式的转化、连接、汇聚、窗口化、状态等。Flink为这些API封装并提供了各种数据类型类。之前说了低层次抽象中的Process Function嵌入到了DataStream APi中,这样某些操作可以直接使用Process Function,而DataSet API则为有限数据集提供了额外的处理原语,比如循环和迭代
  • Table API:围绕表构建的声明式DSL,可以动态变更表数据(当使用流来表示)。 Table API遵循并扩展了关系模型:表对应了一个schema,而API提供了类似于select,project,join,groupBy,aggregate这样的操作。Table API程序以声明的方式定义了逻辑操作要做的事情,而不是指定操作代码应该如何编写。虽然用户可自定义函数来扩展Table API,但它能表达的语义比起Core API还是要少一些,只是使用上更方便而已。另外Table API在执行前会做一些优化。Flink支持无缝地对table和DataStream/DataSet进行转换,这样一个程序可以混用Table API和DataStream/DataSet
  • SQL:最高层次抽象。在语义和表达方式上都类似于Table API,只是以SQL查询的方式来构建程序。它可以与Table API进行交互,比如直接查询Table API中定义的表

程序与数据流

    Flink程序基础构件就是流(stream)和转换(transformation)。从概念上说,stream就是“永无止境”的消息记录序列,而转换本质上则是一个操作,它接收若干个stream作为输入,并产生新的若干个stream作为输出。

    在执行的是会后,Flink程序会被映射到stream数据流——注意这里的stream数据流 = stream + 操作算子(operator)。每个数据流以若干个source开始,以若干个sink结束——类似于Apache Flume的设计。数据流其实就是一个DAG(有向无环图)。尽管有些情况下允许出现回环,但大多数情况下我们还是可以安全地认为它就是一个DAG。一般的Flink程序可能长成这个样子:

即设置source、设置转换操作算子,然后以添加sink结束。对应数据流DAG就是类似于下图所示:

    程序中的转换与操作算子之间的对应关系通常都是1对1的,当然也可能出现一个转换对应多个操作算子的情况。

并行数据流

    Flink程序本质上是并行的,也是分布式的。在执行期间,一个stream可以有多个stream分区,每个操作算子都可能有若干个操作算子子任务。每个子任务彼此都是独立相关的,且执行在不同的线程中。

    操作算子子任务的数量被称为该操作算子的并行度(parallelism)。一个stream的并行度即创建该stream的操作算子的并行度。不同操作算子可有不同的并行度设置

如上图所示,stream在两个操作算子间传输数据有两种方式:1对1和重分布:

  • 1对1——维护了元素的分区和顺序,即map()操作算子的subtask[1]将按照事件顺序看到Source操作算子的subtask[1]创建的事件
  • 重分布——变更stream分区策略。每个操作算子的subtask发送的数据会被路由到不同的目标subtask上,比如keyBy,broadcast和rebalance。在重分布过程中,元素之间的顺序只会在发送subtask和接收subtask之间被保存。比如上面那个图中,Flink只会维护每个key中的顺序。

窗口(Window)

    在stream上对事件进行汇总与批处理的方式是不同的。举个例子,在stream上统计所有元素的个数几乎是不可能的,因为stream本质上是无限的。相反,stream上进行汇总是以window为单位的,比如统计过去5分钟的数据或计算过去100个元素的总和等。

    Window可以是时间驱动的或数据驱动的。具体的窗口类型还分为固定窗口、滑动窗口和会话窗口。下图就是一个时间窗口的示例:

时间

流式程序中的时间分为3类:

  • event time: 事件被创建的时间,通常由时间戳指定
  • ingestion time:事件进入到Flink数据流Source操作算子的时间
  • processing time:执行基于时间的操作的时间

有状态操作

    数据流中的很多操作都是每次处理一个事件,但有些操作会记住多个事件上的信息——这些操作就被称为是有状态的。有状态操作的状态以KV的形式存储。Flink只允许在keyed stream上访问这部分KV存储,并且被严格地限定在与当前key相关联的值上。将stream上的键与状态对齐可以确保所有的状态变更都是本地操作,这样就保证了不需要事务的开销也能维持一致性。这种对齐还允许Flink重新分布状态并透明地调整stream上的分区策略。

检查点

    Flink是如何实现容错性的呢?即通过stream重演+检查点机制。一个检查点对应于每个输入流的某个点以及对应的状态。一个stream数据流可以从一个checkpoint中恢复而不会破坏一致性(即精确一次处理语义),主要表现为恢复操作算子的状态并从checkpoint处开始重演事件流。检查点的间隔是可以配置的。

流上的Batch

    Flink执行批处理程序的方式只是其流式处理的一个特例。DataSet在内部被当做stream处理的,只是有两个例外:

  • DataSet API中的程序不使用checkpoint,只能以从头重演stream的方式来恢复。这对于DataSet来说是可能的,因为其输入源是有限数据集。不过这把开销推向了恢复端,但是却令常规的数据处理变得轻量级了,毕竟它避免了checkpoint的使用
  • DataSet API中的有状态操作使用简化了的内存数据结构,而不是KV索引机制
  • DataSet API引入了特有的同步化迭代机制,只适用于有限数据集
展开阅读全文
打赏
0
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部