流/批/OLAP一体的Flink引擎介绍(2) | 青训营笔记

207 阅读7分钟

这是我参与「第四届青训营 」笔记创作活动的的第5天

Flink总体架构

一个Flink 集群,主要包含以下两个核心组件:

1.JobManager(JM) :负责整个任务的协调工作包括:调度task、触发协调Task做 Checkpoint、协调容错恢复等;

2.TaskManager(TM) :负责执行一个DataFlowGraph 的各个task 以及data streams的buffer 和数据交换。

SDK或者业务逻辑先在Flink Program处理,处理后的信息提交给JM,JM处理成可以运行的物理执行图发放给不同的work结点(TM)

JobManager职责

  • Dispatcher:接收作业,拉起JobManager来执行作业,并在JobMaster挂掉之后恢复作业;

接收客户端提交的脚本

  • JobMaster:管理一个job 的整个生命周期,会向ResourceManager申谓slot,并将task 调度到对应TM上之后会向RM注册;
  • ResourceManager:负责slot资源的管理和调度,Task manager拉起之后会向RM注册

Flink作业示例

流式的WordCount示例,从kafka中读取一个实时的数据流,每十秒统计一次单词出现次数,DataStream实现代码如下

DataStream<String> lines = env.addSource(new FlinkKafkaConsumer<>(...));
DataStream<Event>events = lines.map((line)->parse(line));
DataStream<Statistics>stats = events
    ·keyBy(event->event.id)
    ·timeWindow(Time.seconds(10))
    ·apply(new MyWindowAggregationFunction());
stats.addSink(new BucketingSink(path));

业务逻辑转换为一个Streaming DataFlow Graph

假设作业的sink算子的并发配置为1,其余算子并发为2

紧接着会将上面的Streaming DataFlow Graph转化 Parallel Dataflow(内部叫Execution Graph)

为了更高效地分布式执行,Flink 会尽可能地将不同的operator链接(chain)在一起形成Task。

这样每个Task可以在一个线程中执行,内部叫做OperatorChain,如下图的source 和map 算子可以 Chain在一起。

最后将上面的Task调度到具体的TaskManager 中的slot中执行,一个Slot 只能运行同一个task的subTask

Flink如何做到流批一体

为什么可以做到流批一体呢?

1.批式计算是流式计算的特例,Everything is Streams有界数据集(批式数据)也是一种数据流、一种特殊的数据流;

因此,理论上我们是可以用一套引擎架构来解决上述两种场景,只不过需要对不同场景支持相应的扩展性、并允许做不同的优化策略。

站在 Flink的角度,Everything is Streams,无边界数据集是一种数据流,一个无边界的数据流可以按时间切段成一个个有边界的数据集,所以有界数据集(批式数据)也是一种数据流。

把批式计算看作特殊的流式计算

因此,不管是有边界的数据集(批式数据)还是无边界数据集,Flink都可以天然地支持,这是 Flink支持流批一体的基础。并且Flink在流批一体上,从上面的API到底层的处理机制都是统一的,是真正意义上的流批一体。

Apache Flink主要从以下几个模块来做流批一体

1.SQL层;

类似关系型的SPA,支持有边界和无边界数据集的处理

  1. DataStream API层统一,批和流都可以使用 DataStream API来开发;

3.Scheduler层架构统一,支持流批场景

  1. Failover Recovery层架构统一,支持流批场景;

容错处理是支持批和流一些差异上的支持

  1. Shuffle Service层架构统一,流批场景选择不同的 Shuffle Service;
流批一体的Scheduler层

\

把逻辑的DAG图转化为物理执行图

Scheduler 主要负责将作业的DAG转化为在分布式环境中可以执行的Task

在1.12之前的Flink版本中,Flink支持以下两种调度模式

模式特点场景
EAGER申请一个作业所需要的全部资源,然后同时调度这个作业的全部Task,所有的Task 之间采取Pipeline的方式进行通信Stream作业场景
LAZY先调度上游,等待上游产生数据或结束后再调度下游,类似 Spark的 Stage 执行模式。Batch作业场景

EAGER模式:先拿到全部作业,再去调度全部的Task

LAZY模式:把DAG图分为上游和下游,先调度上游,释放完上游后调度下游

  • EAGER模式

    •   某个作业中,A算子和B算子有两个并发,C算子和D算子有四个并发,A和B之间是点对点的连接,B的数据平均分给C,C和D之间通过哈希值传递
    • 12个task会一起调度,集群需要有足够的资源
  • LAZY模式

    • 最小调度一个task即可,集群有1个slot资源可以运行
  • 最新的Flink调度机制

    • 由Pipeline的数据交换方式连接的Task构成为一个 Pipeline Region;

    • 本质上,不管是流作业还是批作业,都是按照Pipeline Region粒度来申请资源和调度任务。

    • ALL_EDGES_BLOCKING:

      • 所有Task之间的数据交换都是BLOCKING 模式;

        • A产生的数据不会立刻给到B,而是先写入磁盘

      • 分为12pipeline region;

    • ALL_EDGES PIPELINED:

      • 所有Task之间的数据交换都是PIPELINE模式;
      • 分为1个pipeline region;
流批一体的Shuffle Service层

Shuffle:在分布式计算中,用来连接上下游数据交互的过程叫做 Shuffle。

实际上,分布式计算中所有涉及到上下游衔接的过程,都可以理解为 Shuffle。

  • 针对不同的分布式计算框架,Shuffle通常有几种不同的实现:

1.基于文件的Pull Based Shuffle,比如Spark 或MR,它的特点是具有较高的容错性,适合较大规模的批处理作业,由于是基于文件的,它的容错性和稳定性会更好些;

2.基于 Pipeline的 Push Based Shuffle,比如Flink、Storm、Presto等,它的特点是低延迟和高性能,但是因为shuffle数据没有存储下来,如果是batch 任务的话,就需要进行重跑恢复;

  • 流和批 Shuffle之间的差异:
  1. Shuffle 数据的生命周期:流作业的Shuffle 数据与Task是绑定的,而批作业的Shuffle数据与Task是解耦的;

  2. Shuffle 数据存储介质:流作业的生命周期比较短、而且流作业为了实时性,Shuffle通常存储在内存中,批作业因为数据量比较大以及容错的需求,一般会存储在磁盘里;

  3. Shuffle 的部署方式:流作业 Shuffle服务和计算节点部署在一起,可以减少网络开销,从而减少latency,而批作业则不同。

Flink对于流和批提供两种类型的 Shuffle,虽然 Streaming和 Batch Shuffle在具体的策略上存在一定的差异,但本质上都是为了对数据进行Re-Partition,因此不同的 Shuffle 之间是存在一定的共性的。

所以 Flink的目标是提供一套统一的 Shuffl架构,既可以满足不同 Shuffle在策略上的定制,同时还能避免在共性需求上进行重复开发。

  • 在Streaming 和OLAP场景

    • 为了性能的需要,通常会使用基于Pipeline的Shuffle模式
  • 在Batch 场京

    • 一般会选取Blocking的 Shuffle模式

为了统一Flink在 Streaming和 Batch 模式下的Shuffle架构,Flink 实现了一个 Pluggable的 ShuffleService 框架,抽象出一些公共模块。

对于 Shuffle Service,Flink开源社区已经支持:

1.Netty Shuffle Service:既支持pipeline又支持blocking,Flink 默认的shuffle Service策略;

  1. Remote Shuffle Service:既支持pipeline 又支持blocking,不过对于pipeline模式,走remote反而会性能下降,主要是有用在batch的blocking场景,字节内部是基于CSS实现的 RSS。

总结

  • 经过相应的改造和优化之后,Flink在架构设计上,针对DataStream层、调度层、Shuffle、Service层,均完成了对流和批的支持。
  • 至此,业务已经可以非常方便地使用Flink解决流和批场景的问题了。