本篇文章大概2515字,阅读时间大约7分钟
翻译Flink官网关于flink运行架构及编程模型的内容,本文的图片来自flink官网。计划今年下半年将flink应用到生产环境,最近在进行flink的学习,会翻译官方文档的部分内容
1
Flink分布式运行环境
任务和Operator链
在分布式执行时,flink会把operator的subtask链接成一个task。每个task会被一个线程进行执行。链接operator到一个task中是一个有效的优化手段:减少了线程切换和缓存的开销,在降低延迟的同时提高了吞吐量。算子链行为是可以进行配置的。
可以进行operator chain的条件:
- 上下游算子并行度一致
- 上下游算子之间不存在shuffle
以上任务是一个典型的数据处理应用,soruce-transforma-sink的结构,在并行视角下,一共存在5个subtask,也就是需要5个线程去执行。其中source和map通过算子链链接在一起。
Job Managers Task Managers Clients
Flink运行时的角色,也就是存在的进程
- JobManager - master协调分布式执行,进行task调度,执行checkpoint,执行容灾恢复,管理集群中的TaskManager
- 至少存在一个JobManager,高可用模式下,一主多从
- TaskManager - worker执行数据流的task(具体来说是subtask),执行数据流的分发和交换工作,并将节点上的资源信息和任务运行情况汇报上JobManager
- 至少存在一个TaskManager
- Client - 客户端并不是flink运行环境的一部分,而是将dataflow根据用户选择模式提交到JobManager。提交应用之后,客户端可以选择断开连接还是保持连接状态。
Task Slots和资源
每个worker-TaskManager都是一个JVM进程,在独立的线程中去执行一个或多个subtask。为了控制一个worker可以接收的任务数量,TaskManager中存在Task Slots的概念。
每个task slot代表了TaskManager的固定资源子集。如果一个TaskManager存在3个slot,则每个slot分到该TaskManager 1/3的资源。不同的task会在不同的slot中执行,可以有效避免资源竞争。slot的资源隔离是内存级别的,对CPU无效。同一个JVM中的任务共享TCP连接和心跳,共享数据和数据结构,可以有效减少每个任务的开销。
默认情况下,Flink允许subtask共享slot,其中的subtask来自同一个job即可。也就是说,一个slot甚至可以容纳整个job。给定taskmanager的slot数量,相当于规定了taskmanager的并发执行能力上限
- flink集群所需的slot与job中的最高并行度一样多,便于在提交flink应用的时候设置资源申请情况
- 资源利用率更高
比如上图中的一个任务,存在两个TaskManager,整个任务中的最高并行度是6,而sink的并行度为1。
经验值:task slot数量=机器CPU核心数量
2
Flink中的核心概念
编程抽象
Flink针对批和流应用提供了不同级别的编程抽象
- 最低级别的抽象是stateful streaming,通过process function嵌入到datastream api中,可以实现复杂计算
- 一般的计算应用不需要使用低级别的抽象进行编程,而是使用Core api即datastream和dataset api,可以利用core api实现关联,聚合,窗口,状态等操作。process function和datastream api混合编程,可以实现精细化的计算逻辑
- table api是声明式式的编程模型,具有schema,以声明式的方式定义了逻辑操作。并且flink会在执行table api编写代码之前进行优化
- Flink SQL是最高级别的编程抽象,SQL api可以查询通过table api定义的表。
并行数据流
flink应用由stream和transformations构成,flink应用是并行和分布式执行的。在执行过程中,stream存在一个或多个分区,而每个opeator存在一个或多个subtask。operator的subtask是相互独立的,由不同的线程执行,运行在不同的机器或容器中。flink允许一个job的不同operator具有不同的并行度。
streams可以在两个operator间进行数据流转,数据流转模式分为两类:one-to-one模式,redistributing模式
- one-to-one,stream会保持元素的分区和排序,如source和map看到的元素顺序和分区是一致的,类似spark中的窄依赖
- redistributing,stream的分区会发生改变。operator的subtask将数据发送到不同的目标subtask中,具体的分发策略由算子决定。类似spark中的宽依赖,也就是存在shuffle
窗口
在流处理中进行所有元素的聚合计算是不现实的,因为流是无界的。流上的聚合是需要进行窗口划分的,如统计过去5分钟的总数和最近100个元素的和。
flink中的窗口可以通过时间驱动或数据驱动,常用的有滚动窗口(数据无重叠),滑动窗口(数据有重叠)和会话窗口。
时间
Flink支持三种不同类型的时间概念
- event time - 事件时间,事件时间是事件真实发生的时间。一般是事件消息中的一个时间戳字段,flink通过timestamp assigner访问事件时间
- ingestion time - 摄取时间,事件进入到source的时间点,使用场景较少
- process time - 处理时间,事件进入各个operator的时间点,也就是说时间的概念在整个流中是不一致的,整个过程不需要数据流和计算框架进行时间协调,拥有最好的性能和最低的延迟,不确定性较高