Apache Flink-编程指南-概念-编程模型

数据流编程模型

  • 抽象层级
  • 程序和数据流
  • 并行数据流
  • 窗口
  • 事件
  • 状态化计算
  • 为容错的检查点
  • 流之上的批处理
  • 下一步

抽象层级

Flink为开发流/批处理应用程序提供不能层级的抽象。

Programming levels of abstraction

  • 最低级别的抽象简单提供状态化流处理。通过Process Function嵌入到DataStream API中。允许用户使用一致性容错状态来自由处理来自一个或多个流的事件。此外,用户可以注册事件时间并处理时间回调,允许程序实现复杂的计算。

  • 在实际应用中, 大多数应用程序不会使用如上描述的低级别的抽象,而是使用Core APIs如DataStream API (有界和无界流) 和DataSet API (有界数据集). 这些经常用到的APIs为数据处理提供了通用的构建块,如各种形式的用户自定义 transformations, joins, aggregations, windows, state, 等等. 这些被处理的数据类型在各自不同的编程语言中被称之为类。

    低级别Process FunctionDataStream API集成在一起, 使得可以对低级别抽象进行操作。DataSet API提供额外的原生操作针对于有界数据集,如循环/迭代。

  • Table API是一个以表为中心的申明式DSL,它可以动态的改变表 (当表示流时).。Table API 遵循如下 (扩展) 关系模型:表有一个关联的schema (类似关系型数据库中的表) , API 提供操作, 比如 select, project, join, group-by, aggregate, 等等。Table API程序显示申明定义应该做什么样的逻辑而不是详细的说明操作的代码是什么样的。尽管Table API的扩展性是通过各种类型的自定义函数支持的, 但它的表达能力不如Core APIs, 但是更加易用 (写更少的代码)。除此之外,Table API程序还会在应用执行前通过优化器优化规则。

    表和DataStream/DataSet可以无缝转换,允许程序混合Table API 和DataStream and DataSet APIs.

  • Flink提供最高级别的抽象是SQL。在语义和表现形式上和Table API类似,但是以SQL查询的应用程序。SQL抽象和Table API很接近,SQL查询可以被执行在定义了Table API的表之上。

程序和数据流

Flink程序基本的构建块是流和转换。 (注意DataSet API中使用的数据集内部也是流-稍后介绍) 从概念上说,一个流是数据记录流 (本质没有结束) , 转换操作是把一个或多个流作为输入,结果产生一个或多个输出流。

当执行的时候,Flink应用程序被映射成流动的流,由流和转换操作组成。 每一个数据流由一个或多个源开始以一个或多个sink结束。数据流类似有向无环图(DAGs)。尽管允许通过迭代构建特殊形式的环, 但为简单起见,大多数情况下我们忽视它。

A DataStream program, and its dataflow.

通常程序中的转换和数据流中的操作是一对一关系。有时候,一个转换可能由多个转换操作组成。

Sources和sinks在 streaming connectorsbatch connectors文档中有说明。转换在DataStream operatorsDataSet transformations中有说明。

并行流

Flink程序内部是并行和分布式运行的。在运行期间,一个流有一个或多个分区,每一个操作有一个或多个操作子任务。操作子任务之间是独立的,可能运行在不同的机器或container以不同的线程运行。

指定操作的操作子任务数量称为并行度。流的并行度总是由操作产生。相同程序的不同操作可能有不同级别的并行度。

A parallel dataflow

流可以两个操作之间以一对一的模式传输数据,或重分布模型:

  • 一对一流(例如上图中的Sourcemap()操作之间) 保留 了分区并且元素是有序的。意味着map()操作中的subtask[1]可以看到由source操作subtask[1]产生的相同顺序的相同元素。

  • 重分布流 (如上面的map()keyBy/window之间, 也如同keyBy/windowSink)改变了流的分区。每一个操作子任务将数据发送到不同目标的子任务,依赖于所选择的转换操作。例如 keyBy() (对key进行hash重分区), broadcast(), rebalance() (随机重)。 在重分布式交换中元素之间的顺序仅仅在每一对发送和接受的子任务重保留I (例如map()的subtask[1]和keyBy/window的subtask[2])。因此在这样的例子中, 每个key的顺序是保留的,但是并行度的引入确实给从不同key顺序聚合到结果带来了不确定性因素。

关于配置和控制并行度的细节可以在parallel execution文档中找到。

窗口

聚合事件(例如counts, sums)在批处理和流处理中的工作方式是不同的。例如,在一个流中统计所有元素是不可能的,因为流畅通常是无限的(无界)。因此,流上的聚合 (counts, sums, 等等), 是有窗口作用域的,例如“统计5分钟内”, 或 “100个元素总和 ”。

窗口可以使时间驱动的 (例如每30秒) ,也可以是数据驱动(例如每100个元素)。一个典型的区别不同窗口类型的方式如,  tumbling windows (没有重复), sliding windows (有重复), and session windows (不间断产生不活动的间隙)。

Time- and Count Windows

更多窗口例子可以查看 blog post。更多细节可以查看window docs

时间

当谈到流程序中的事件(例如定义窗口),有不同概念的事件:

  • 事件时间 事件被创建时候的时间,通常在事件中由一个时间戳描述,例如,有生产传感器附加上去,或服务添加。Flink通过时间戳assigners访问事件时间。 

  • 摄入时间 是事件在进入Flink数据流的source操作时的时间。

  • 处理时间 每个操作在执行基于时间的操作符的本地时间。

Event Time, Ingestion Time, and Processing Time

更多如何处理时间的细节参考 event time docs

状态化操作

事件流里的许多操作简单的看是一次处理一个事件(例如事件解析器),有些操作记住跨多个事件的信息(例如窗口操作)。这些操作称为状态化。

状态操作的状态可以被认为像嵌入key/value存储一样被维护。状态被分区并严格地分布在有状态操作符读取的流中。因此,访问key/value状态只能在有键的流中,在keyBy()函数之后,仅限于与当前事件key关联的值。流的key和状态确保所有的状态更新是本地操作,确保一致性不需要事务开销,这种对齐还允许Flink重新分配状态并透明地调整流分区。

State and Partitioning

更多信息参看文档 state

为容错的检查点

Flink使用流回放和检查点的组合来实现容错。检查点与输入流中的每一个特定点相关,和每个操作的状态一致。在维护一致性时流可以从一个重用检查点(正好一次处理语义)通过重新载入操作的状态和从坚持点的点回放事件。

检查点间隔是在执行期间恢复容错开销的恢复时间(需要回放事件的数量)。

关于检查点间隔及相关主题,使用及配置在checkpointing API docs中。

流之上的批处理

Flink执行批处理程序作为流程序的特殊情况,其中流是有界的(有限数量的元素)。数据集在内部被视为数据流。因此,上面的概念以同样的方式应用于批处理程序,并且适用于流媒体程序,只有少数例外:

  • 批处理程序的容错能力不使用检查点。恢复是通过完全重新播放流来实现的。这是可能的,因为输入是有界的。这将成本推高到恢复的程度,但使常规处理更便宜,因为它避免了检查点。

  • DataSet API中的有状态操作使用简化的内存/核心数据结构,而不是键/值索引。

  • DataSet API引入了特殊的同步(基于超级步骤的)迭代,只有在有界流上才有可能。有关详细信息,请查看迭代文档。

下一步

Continue with the basic concepts in Flink’s Distributed Runtime.

猜你喜欢

转载自blog.csdn.net/javajxz008/article/details/83025851