项目说要做创新活动,所以需要把原来的流处理从Spark替换成Flink。嗯,学习Flink!本文所用flink版本为1.6。
Flink基础概念
程序和数据流-Programs and Dataflows
官网地址:Programs and Dataflows。
- Flink基本构建块:
- Streams-中间结果
- Transformations-以一个或多个stream作为输入的某种operation,输出一个或多个result stream。
- 运行的程序-映射成-Streaming Dataflows
Dataflow:由一组stream和transformation Operators组成。由一个或多个Source开始,一个或多个Sink结束。Dataflow类似与DAG。
当编写好的一个Flink程序运行的时候,会被映射成Streaming Dataflow。程序中写的transformation和dataflow中的operator关系一般1:1,也可能1:n。
并行数据流-Parallel Dataflows
官网地址:Parallel Dataflows。
- 程序在Flink内部的执行具有并行、分布式的特性:
- stream-被分割成stream partition:一个Stream可以被分成多个Stream分区(Stream Partitions)。
- operator-被分割成operator subtask:一个Operator可以被分成多个Operator Subtask,每一个Operator Subtask是在不同的线程中独立执行的。
一个Operator的并行度=Operator Subtask的个数
一个Stream的并行度=生成它的Operator的并行度(The parallelism of a stream is always that of its producing operator.)
一个程序中,不同的operator可能具有不同的并行度。 - 数据传输形式
Stream在operator之间传输数据的形式可以是one-to-one(forwarding)的模式也可以是redistributing的模式。
官网示例图:
- One-to-one模式:One-to-one streams保持着分区以及元素的顺序。比如从Source[1]到map()[1],它保持了Source的分区特性(Partitioning)和分区内元素处理的有序性,也就是说map()[1]的Subtask看到数据流中记录的顺序,与Source[1]中看到的记录顺序是一致的。
- Redistribution模式:Redistributing streams 的分区会发生改变,改变了输入数据流的分区。比如从map()[1]、map()[2]到keyBy()/window()/apply()[1]、keyBy()/window()/apply()[2],每个上游的operator subtask向下游的多个不同的subtasks发送数据,这与选择的transformation有关(比如说keyBy() (基于hash码重分区),broadcast()或者rebalance()(随机redistribution))。在一个redistribution的交换中,只有每一对的发送、接收subtask间的elements顺序才会被维持(比如说,subtask[1] of map() and subtask[2] of keyBy/window)。So in this example, the ordering within each key is preserved, but the parallelism does introduce non-determinism regarding the order in which the aggregated results for different keys arrive at the sink。
并且在本例中,Source Operator对应2个Subtask,所以并行度为2,而Sink Operator的Subtask只有1个,故而并行度为1。
窗口-Windows
官网地址:Windows。
窗口可以是时间驱动的(比如,每30秒)也可以是数据驱动的(比如,每100个元素)。通常我们将窗口划分为:tumbing windows(不重叠),sliding windows(有重叠)和session windows(有空隙的活动)。
时间-Time
官网地址:Time。
- Event Time:事件的创建时间,通常通过事件中的一个时间戳来描述。
- Ingestion time:事件进入Flink 数据流的source的时间。
- Processing Time:Processing Time表示某个Operator对事件进行处理时的本地系统时间(是在TaskManager节点上)。
有状态的数据操作-Stateful Operations
官网地址:Stateful Operations。
在流处理中,有些操作仅仅在某一时间针对单一事件(如事件转换map),有些操作需要记住多个事件的信息并进行处理(window operators),则后面这些操作称为有状态的操作。
有状态的操作,其状态被维护的地方,可以将其看作是一个内嵌的key/value存储器。这些状态信息会跟数据流一起分区并且分布存储,并且可以通过有状态的数据操作来访问。因此这些key/value的状态信息仅在keyed streams(通过keyBy() 函数处理过)中才能访问到,并且只能根据当前事件的key来访问其值。数据流按照key排列能保证所有的状态更新都是本地操作,保证一致性且无事务问题。同时这种排列方式使Flink能够透明的再分发状态信息和调整数据流分区。
关于Flink有状态的流的工作,可以详细参考文章:Flink 有状态的流的工作-Working with state。
容错的Checkpoint-Checkpoints for Fault Tolerance
官网地址:Checkpoints for Fault Tolerance。
Flink 通过流回放(stream replay)和设置检查点(checkpointing)的方式实现容错。
一个checkpoint关联了输入流中的某个记录和相应状态operators。数据流(a streaming dataflow)可以从checkpoint中进行恢复,通过恢复Operators的状态以及从该checkpoint重放事件,其保证一致性(exactly-once 的处理语义)。
Checkpoint的间隔关系到执行时的容错性和恢复时间,也决定了需要被重放的事件数。
流上的批处理-Batch on Streaming
官网地址:Batch on Streaming。
Flink把批处理作为特殊的流处理程序来执行,将其看作有界的流(有限数量的元素)。
DataSet在内部被当作一个流数据,因此上面的适用于流处理的这些概念在批处理中同样适用,除了一些小的不同:
- 容错机制不同:批处理的容错机制不使用checkpoints,恢复机制是通过完整的流重放来实现。这是因为inputs是有界的,它将开销更多地引入到恢复操作上,但另一方面也使得运行时的常规流程代价更低,因为它规避了检查点机制。
- 有状态的Operations数据结构不同:DataSet API的有状态操作API使用简单的内存和堆外内存(in-memory/out-of-core)的数据结构,而不是key/value的索引。
- DataSet API中引入一种独特的同步的迭代操作(superstep-based),这个仅应用于有界数据流。详见Iterations。
至此,本篇内容完成。