DataStreamAPI实践原理——计算模型
引入
Apache Flink 是一个框架和分布式处理引擎,用于在 无边界 和 有边界 数据流上进行有状态的计
算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。Flink可以处理批数据也可以处理流数据,本质上,流处理是Flink中的基本操作,流数据即无边界数据流,在Flink中处理所有事件都可看成流事件,批数据可以看成是一种特殊的流数据,即有边界数据流,这与Spark计算框架截然相反,在Spark中批处理是最基本操作,流事件可以划分为一小批一小批数据进行微批处理,来达到实时效果,这也是两者区别之一。
- 无界流:有定义流的开始,但没有定义流的结束。它们会无休止地产生数据。无界流的数据必须持续处理,即数据被摄取后需要立刻处理。我们不能等到所有数据都到达再处理,因为输入是无限的,在任何时候输入都不会完成。处理无界数据通常要求以特定顺序摄取事件,例如事件发生的顺序,以便能够推断结果的完整性。
- 有界流:有定义流的开始,也有定义流的结束。有界流可以在摄取所有数据后再进行计算。有界流所有数据可以被排序,所以并不需要有序摄取。有界流处理通常被称为批处理。
Apache Flink 擅长处理无界和有界数据集,精确的时间控制和状态化使得 Flink 的运行时(runtime)能够运行任何处理无界流的应用。有界流则由一些专为固定大小数据集特殊设计的算法和数据结构进行内部处理,产生了出色的性能。
在分布式计算框架中,需要处理分散在多台机器上的海量数据,对于大数据开发人员面临最大的挑战就是代码的编写、部署、调度、容错等。Flink 提供了不同的抽象级别以开发流式或批处理应用:
- 最顶层:SQL/Table API 提供了操作关系表、执行SQL语句分析的API库,供我们方便的开发SQL相关程序
- 中层:流和批处理API层,提供了一系列流和批处理的API和算子供我们对数据进行处理和分析
- 最底层:运行时层,提供了对Flink底层关键技术的操纵,如对Event、state、time、window等进行精细化控制的操作API
DataStreamAPI实践原理的重点在于Flink提供了的抽象流集即 DataStream,开发者只要调用统一的编程API,传入具体的计算逻辑,不必太多关心底层的细节,就可以完成各种复杂的计算了,并且可以实现快速部署、资源调度、任务容错等,大大的提高了开发效率。
Flink计算模型
DataStream是Flink流式计算编程的抽象数据集(与Spark的RDD是类似的),抽象数据集里面不装要真正要计算的数据,而是记录一些描述信息,例如从哪里读取数据,掉了用了什么方法,传入了什么计算逻辑,通过调用DataStreamTransformation(s)和Sink后,构建成执行计划图DataFlow Graph(类似Spark的DAG),然后生成Task提交到集群中执行真正的计算逻辑。通过前面实时计算核心论文系列文章,我们知道Flink实时计算模型主要分为数据源、转换操作和数据输出三部分。
- 数据源:关注与外部数据系统的打通,读取消息、中间件等数据
- 转换操作:关注数据的转换,包括filter、transform和connect操作
- 数据输出:将转换后的数据输出到外部数据系统,供用户获取
在开发Flink实时计算程序,首先学要创建StreamExecutionEnvironment,然后调用相应的Source算子创建原始的DataStream,再调用零到多次Transformation(转换算子),每调用一次Transformation都会生成一个新的DataStream,最后调用Sink,我们写的程序就形成一个Data Flow Graph(数据流图),然后提交给JobManager,经过优化后生成包含有具体计算逻辑的Task实例,然后调度到TaskManager的slot中开始计算。
Data Source数据源
在实时计算DataStream API中,Source是用来获取外部数据源的操作,按照获取数据的方式,可以分为:基于集合的Source、基于Socket网络端口的Source、基于文件的Source、第三方Connector Source和自定义Source五种。
前三种Source是Flink已经封装好的方法,这些Source只要调用StreamExecutionEnvironment的对应方法就可以创建DataStream了,使用起来比较简单,我们在学习和测试的时候会经常用到。如果以后生产环境想要从一些分布式、高可用的消息中间件中读取数据,可以使用第三方Connector Source,比如Apache Kafka Source、AWS Kinesis Source、Google Cloud PubSub Source等(国内公司使用比较多的是Kafka这个消息中间件作为数据源),使用这些第三方的Source,需要额外引入对应消息中间件的依赖jar包。于此同时Flink允许开发者根据自己的需求,自定义各种Source,只要实现SourceFunction这个接口,然后将该实现类的实例作为参数传入到StreamExecutionEnvironment的addSource方法就可以了,这样大大的提高了Flink与外部数据源交互的灵活性。
从并行度的角度,Source又可以分为非并行的Source和并行的Source。非并行的Source它的并行度只能为1,即用来读取外部数据源的Source只有一个实例,在读取大量数据时效率比较低,通常是用来做一些实验或测试,例如Flink的Socket网络端口读取数据的Source就是一个非并行的Source;并行的Source它的并行度可以是1到多个,即用来读取外部数据源的Source可以有一个到多个实例(在分布式计算中,并行度是影响吞吐量一个非常重要的因素,在计算资源足够的前提下,并行度越大,效率越高)。例如Kafka Source就是并行的Source。
Transformation转换算子
Transformation翻译成中文意为转换,是将一个或多个DataStream调用某个转换算子,生成一个新的DataStream,原来的DataStream不变。Flink程序可以将多个Transformation生成的DataStream组合成一个复杂的DataFlow拓扑。这里所提到的转换算子,其实就是DataStream的转换方法,调用转换算子后,一定会生成一个新的DataStream。
我们前面的内容提到过,DataStream其实是一个抽象的数据集,调用了DataStream的转换算子,并不会立即触发任务的执行,对于Flink程序而言,仅是记录了调用了哪个方法,传入了具体什么处理逻辑,这些转换操作会生成多个有着依赖关系和先后顺序的DataStream,这些DataStream组成了DataFlow拓扑(类似Spark的DAG有向无环图),这个DataFlow其实就是一个任务的逻辑执行计划,Flink最终会将这个逻辑计划转成真正的物理计划,最后提交到集群中运行。
Data Sink 数据输出
经过一系列Transformation转换操作后,最后一定要调用Sink操作,才会形成一个完整的DataFlow拓扑。只有调用了Sink操作,才会产生最终的计算结果,这些数据可以写入到的文件、输出到指定的网络端口、消息中间件、外部的文件系统或者是打印到控制台。