Apache Flink术语Flink计算框架可以处理批数据也可以处理流式数据Flink将批处理看成是流处理的一个特例认为数据原本产生就是实时的数据流这种数据叫做无界流unbounded stream无界流是持续不断的产生没有边界批数据只是无界流中的一部分叫做有界流bounded stream针对无界流数据处理叫做实时处理,这种程序一般是7*24不间断运行的针对有界流数据处理叫做批处理这种程序处理完当前批数据就停止。下面我们结合一些代码介绍Flink中的一些重要的名词术语。Application与Job无论处理批数据还是处理流数据我们都可以使用Flink提供好的Operator算子来转换处理数据一个完整的Flink程序代码叫做一个Flink Application像前面章节我们编写的Flink读取Socket数据实时统计WordCount代码就是一个完整的Flink Application一个完整的Flink Application一般由Source(数据来源)、Transformation转换、Sink数据输出三部分组成Flink中一个或者多个Operator(算子)组合对数据进行转换形成Transformation一个Flink Application 开始于一个或者多个Source结束于一个或者多个Sink。编写Flink代码要符合一定的流程首先我们需要创建Flink的执行环境Execution Environment,然后再加载数据源Source对加载的数据进行Transformation转换进而对结果Sink输出最后还要执行env.execute()来触发整个Flink程序的执行编写代码时将以上完整流程放在main方法中形成一个完整的Application。一个Flink Application中可以有多个Flink Job每次调用execute()或者executeAsyc()方法可以触发一个Flink Job ,一个Flink Application中可以执行多次以上两个方法来触发多个job执行。但往往我们在编写一个Flink Application时只需要一个Job即可。DataFlow数据流图一个Flink Job 执行时会按照Source、Transformatioin、Sink顺序来执行这就形成了Stream DataFlow(数据流图)数据流图是整体展示Flink作业执行流程的高级视图通过WebUI我们可以看到提交应用程序的DataFlow。像之前提交的Flink 读取Socket数据实时统计WordCount在WebUI中形成的DataFlow如下可以看到对应的Source、各个转换算子、Sink部分。通常Operator算子和Transformation转换之间是一对一的关系有时一个Transformation转换中包含多个Operator形成一个算子链这主要取决于数据之间流转关系和并行度是否相同关于算子链内容在4.5.4部分再做介绍。Subtask子任务与并行度在集群中运行Flink代码本质上是以并行和分布式方式来执行这样可以提高处理数据的吞吐量和速度处理一个Flink流过程中涉及多个Operator每个Operator有一个或者多个Subtask子任务不同的Operator的Subtask个数可以不同一个Operator有几个Subtask就代表当前算子的并行度Parallelism是多少Subtask在不同的线程、不同的物理机或不同的容器中完全独立执行。上图下半部分是多并行度DataFlow视图Source、Map、KeyBy等操作有2个并行度对应2个subtask分布式执行Sink操作并行度为1只有一个subtask一共有7个Subtask每个Subtask处理的数据也经常说成处理一个分区Stream Partition的数据。 一个 Flink Application 的并行度通常认为是所有Operator中最大的并行度 。上图中的Application并行度就为2。Flink中并行度可以从以下四个层面指定Operator Level (算子层面算子层面设置并行度是给每个算子设置并行度直接在算子后面调用.setparallelism()方法写入并行度即可只是针对当前算子有效注意一些算子不能设置并行度例如keyBy 返回的对象是KeyedStream这种分组操作无法设置并行度socketTextStream是非并行source只支持1个并行度也不能设置并行度。Operator Chains 算子链在Flink作业中用户可以指定Operator Chains(算子链)将相关性非常强的算子操作绑定在一起这样能够让转换过程上下游的Task数据处理逻辑由一个Task执行进而避免因为数据在网络或者线程间传输导致的开销减少数据处理延迟提高数据吞吐量。默认情况下Flink开启了算子链。例如下图流处理程序Source/map就形成了一个算子链keyBy/window/apply形成了以算子链分布式执行中原本需要多个task执行的情况由于有了算子链减少到由5个Subtask分布式执行即可。那么在Flink中哪些算子操作可以合并在一起形成算子链进行优化这主要取决于算子之间的并行度与算子之间数据传递的模式。一个数据流在算子之间传递数据可以是一对一One-to-one的模式传递也可以是重分区Redistributing的模式传递两者区别如下One-to-one一对一传递模式(例如上图中的Source和map()算子之间)保留了元素的分区和顺序类似Spark中的窄依赖。这意味着map()算子的subtask[1]处理的数据全部来自Source的subtask[1]产生的数据并且顺序保持一致。例如map、filter、flatMap这些算子都是One-to-one数据传递模式。Redistributing重分区模式(如上面的map()和keyBy/window之间以及keyBy/window和Sink之间)改变了流的分区这种情况下数据流向的分区会改变类似于Spark中的宽依赖。每个算子的subtask将数据发送到不同的目标subtask这取决于使用了什么样的算子操作例如keyBy()是分组操作会根据key的哈希值对数据进行重分区再如window/apply算子操作的并行度为2流向了并行度为1的sink操作这个过程需要通过rebalance操作将数据均匀发送到下游Subtask中。这些传输方式都是重分区模式Redistributing。在Flink中 One-to-one 的算子操作且并行度一致,默认自动合并在一起形成一个算子链 由一个task执行对应逻辑。我们也可以通过代码禁用算子链或者进行细粒度的控制哪些算子可以合并形成算子链。通过以下方式来禁用算子链Flink执行图Flink代码提交到集群执行时最终会被转换成task分布式的在各个节点上运行在前面我们学习到DataFlow数据流图DataFlow是一个Flink应用程序执行的高级视图展示了Flink应用程序执行的总体流程在Flink底层由DataFlow最终转换成执行的task的过程还涉及一些对象转换。下图以一个普通的Flink处理数据流程展示了一个Flink任务提交到集群后内部对象转换关系和流程其中每个虚线框代表一个taskp代表并行度这里假设为2。首先编写好的代码提交后在客户端会按照Transformation转换成StreamGraph任务流图StreamGraph是没有经过任何优化的流图展示的是程序整体执行的流程。StreamGraph进而会按照默认的Operator Chains算子链合规则转换成JobGraph作业图在JobGraph中会将并行度相同且数据流转关系为One-to-one关系的算子合并在一起由一个Task进行处理原本2个Task处理的逻辑这一步转换一般也是在客户端进行。JobGraph会被提交给JobManager最终由JobManager中JobMaster转换成ExecutionGraph执行图ExecutionGraph中会按照每个算子并行度来划分对应的Subtask每个Subtask最终再次被转换成其他可以部署的对象发送到TaskManager上执行。以上整体流程就是Flink 任务在底层执行转换的流程基于以上流程我们可以得到以下结论在Flink中一个Task一般对应的就是一个算子或者多个算子逻辑。多个算子逻辑经过Operator Chains优化后也是由一个Task执行的。Flink分布式运行中Task会按照并行度划分成多个Subtask每个Subtask由一个Thread线程执行多个Subtask分布在不同的线程不同节点形成Flink分布式的执行。Subtask是Flink任务调度的基本单元。