Flink概述

前言

Spark streaming 可以说是根据时间分区的批处理系统。Fink 则是基于事件驱动的。 也就是说可以把时间定义成事件,也可以把数量定义成事件…也就是说Fink的场景更灵活。

安装

flink的安装方式有几种,这边选用docker的方式

docker pull flink:latest
docker run -it -d  --name fink -p 8081:8081  flink:latest  /bin/bash 

架构

Flink 运行时由两种类型的进程组成:一个 JobManager 和一个或者多个 TaskManager。
在这里插入图片描述
Client 不是运行时和程序执行的一部分,而是用于准备数据流并将其发送给 JobManager。之后,客户端可以断开连接(分离模式),或保持连接来接收进程报告(附加模式)。客户端可以作为触发执行 Java/Scala 程序的一部分运行,也可以在命令行进程./bin/flink run …中运行。

可以通过多种方式启动 JobManager 和 TaskManager:直接在机器上作为standalone 集群启动、在容器中启动、或者通过YARN等资源框架管理并启动。TaskManager 连接到 JobManagers,宣布自己可用,并被分配工作。

JobManager

JobManager 具有许多与协调 Flink 应用程序的分布式执行有关的职责:它决定何时调度下一个 task(或一组 task)、对完成的 task 或执行失败做出反应、协调 checkpoint、并且协调从失败中恢复等等。这个进程由三个不同的组件组成:

  • ResourceManager 负责 Flink 集群中的资源提供、回收、分配 - 它管理 task slots,这是 Flink 集群中资源调度的单位(请参考TaskManagers)。Flink 为不同的环境和资源提供者(例如 YARN、Kubernetes 和 standalone 部署)实现了对应的 ResourceManager。在 standalone 设置中,ResourceManager 只能分配可用 TaskManager 的 slots,而不能自行启动新的 TaskManager。

  • Dispatcher 提供了一个 REST 接口,用来提交 Flink 应用程序执行,并为每个提交的作业启动一个新的 JobMaster。它还运行 Flink WebUI 用来提供作业执行信息。

  • JobMaster 负责管理单个JobGraph的执行。Flink 集群中可以同时运行多个作业,每个作业都有自己的 JobMaster。

TaskManagers

TaskManager(也称为 worker)执行作业流的 task,并且缓存和交换数据流。每个 worker(TaskManager)都是一个 JVM 进程,可以在单独的线程中执行一个或多个 task。为了控制一个 TaskManager 中接受多少个 task,就有了所谓的 task slots(至少一个)。

注意此处没有 CPU 隔离;当前 slot 仅分离 task 的托管内存。

通过调整 task slot 的数量,用户可以定义 task 如何互相隔离。每个 TaskManager 有一个 slot,这意味着每个 task 组都在单独的 JVM 中运行。

具有多个 slot 意味着更多 task 共享同一 JVM。同一 JVM 中的 task 共享 TCP 连接(通过多路复用)和心跳信息。它们还可以共享数据集和数据结构,从而减少了每个 task 的开销。

默认情况下,Flink 允许 subtask 共享 slot,即便它们是不同的 task 的 subtask,只要是来自于同一作业即可。

subTask表示算子任务,通过分析subTask发现 keyBy 和 map 操作是不可能并行的,因此可合并在一个solt中

在这里插入图片描述
允许 slot 共享有两个主要优点:

  • Flink 集群所需的 task slot 和作业中使用的最大并行度恰好一样。无需计算程序总共包含多少个 task(具有不同并行度)。
  • 容易获得更好的资源利用。如果没有 slot 共享,非密集 subtask(source/map())将阻塞和密集型 subtask(window) 一样多的资源。通过 slot 共享,我们示例中的基本并行度从 2 增加到 6,可以充分利用分配的资源,同时确保繁重的 subtask 在 TaskManager 之间公平分配。

Flink 应用程序执行

Flink 应用程序的作业可以被提交到长期运行的 Flink Session 集群、专用的 Flink Job 集群 或 Flink Application 集群。这些选项之间的差异主要与集群的生命周期和资源隔离保证有关。

  • Flink Session 集群: 在 Flink Session 集群中,客户端连接到一个预先存在的、长期运行的集群,该集群可以接受多个作业提交。
  • 专用的 Flink Job 集群 :在 Flink Job 集群中,可用的集群管理器(例如 YARN)用于为每个提交的作业启动一个集群,并且该集群仅可用于该作业。
  • Flink Application 集群:当使用生成时,通过 Kubernetes 上部署任何其他应用程序一样部署 Flink 应用程序。

三种方式明显的区别是,资源管理方的不一个。 Flink Session 集群是Fink自己,Flink Job 集群是YARN等资源管理器,Flink Application 集群 则是k8s.

数据&计算模型

同《Spark概述》无太大差别

通过状态快照实现容错处理

快照 – 是 Flink 作业状态全局一致镜像的通用术语。快照包括指向每个数据源的指针(例如,到文件或 Kafka 分区的偏移量)以及每个作业的有状态运算符的状态副本,该状态副本是处理了 sources 偏移位置之前所有的事件后而生成的状态。即节点那时间关键运行数据,可用于重启后恢愎运行状态的

Flink 管理的状态存储在 state backend .Flink 有两种 state backend 的实现 – 一种基于 RocksDB 内嵌 key/value 存储将其工作状态保存在磁盘上的,另一种基于堆的 state backend,将其工作状态保存在 Java 的堆内存中。这种基于堆的 state backend 有两种类型:FsStateBackend,将其状态快照持久化到分布式文件系统;MemoryStateBackend,它使用 JobManager 的堆保存状态快照在这里插入图片描述

快照的操作和原理

  • Checkpoint – 一种由 Flink 自动执行的快照,其目的是能够从故障中恢复。Checkpoints 可以是增量的,并为快速恢复进行了优化。
  • 外部化的 Checkpoint – 通常 checkpoints 不会被用户操纵。Flink 只保留作业运行时的最近的 n 个 checkpoints(n 可配置),并在作业取消时删除它们。但你可以将它们配置为保留,在这种情况下,你可以手动从中恢复。
  • Savepoint – 用户出于某种操作目的(例如有状态的重新部署/升级/缩放操作)手动(或 API 调用)触发的快照。Savepoints 始终是完整的,并且已针对操作灵活性进行了优化。

Flink 使用 Chandy-Lamport algorithm 算法的一种变体,称为异步 barrier 快照(asynchronous barrier snapshotting)。
当 checkpoint coordinator(job manager 的一部分)指示 task manager 开始 checkpoint 时,它会让所有 sources 记录它们的偏移量,并将编号的 checkpoint barriers 插入到它们的流中。这些 barriers 流经 job graph,标注每个 checkpoint 前后的流部分。
在这里插入图片描述

Checkpoint n 将包含每个 operator 的 state,这些 state 是对应的 operator 消费了严格在 checkpoint barrier n 之前的所有事件,并且不包含在此(checkpoint barrier n)后的任何事件后而生成的状态。

当 job graph 中的每个 operator 接收到 barriers 时,它就会记录下其状态。拥有两个输入流的 Operators(例如 CoProcessFunction)会执行 barrier 对齐(barrier alignment) 以便当前快照能够包含消费两个输入流 barrier 之前(但不超过)的所有 events 而产生的状态。

在这里插入图片描述
对齐分为四步:

  1. 算子子任务在某个输入通道中收到第一个ID为n的Checkpoint Barrier,但是其他输入通道中ID为n的Checkpoint Barrier还未到达,该算子子任务开始准备进行对齐。
  2. 算子子任务将第一个输入通道的数据缓存下来,同时继续处理其他输入通道的数据,这个过程被称为对齐。
  3. 第二个输入通道的Checkpoint Barrier抵达该算子子任务,该算子子任务执行快照,将状态写入State Backend,然后将ID为n的Checkpoint Barrier向下游所有输出通道广播。
  4. 对于这个算子子任务,快照执行结束,继续处理各个通道中新流入数据,包括刚才缓存起来的数据。
    在这里插入图片描述

Fink 根据 Barrier 提供精确一次的语义保证时需要进行对齐(Barrier alignment)实现了内部确保精确一次(exactly once)。你也可以根据实际情况进行配置可以产生以下结果:

  • Flink 不会从快照中进行恢复(at most once)
  • 没有任何丢失,但是你可能会得到重复冗余的结果(at least once)
  • 没有丢失或冗余重复(exactly once)

为了实现整条链路的一致性,即端到端精确一次,你需要满足以下条件

  1. 你的 sources 必须是可重放的,并且
  2. 你的 sinks 必须是事务性的(或幂等的)

对于sources的可重放,sink的幂等写,这很好理解。主要本段主要描述事务写。

事务写

简单概括来说,Flink的事务写(Transaction Write)是指,Flink先将待输出的数据保存下来暂时不向外部系统提交,等待Checkpoint结束的时刻,Flink上下游所有算子的数据都是一致时,将之前保存的数据全部提交(Commit)到外部系统。

在这里插入图片描述

如果使用事务写,那只把时间戳3之前的输出提交到外部系统,时间戳3以后的数据(例如时间戳5和8生成的数据)暂时保存下来,等待下次Checkpoint时一起写入到外部系统。这就避免了时间戳5这个数据产生多次结果,多次写入到外部系统。

在事务写的具体实现上,Flink目前提供了两种方式:

  1. 预写日志(Write-Ahead-Log,WAL)
  2. 两阶段提交(Two-Phase-Commit,2PC)。

这两种方式也是很多数据库和分布式系统实现事务时经常采用的协议,Flink根据自身的场景对这两种协议做了适应性调整。这两种方式主要区别在于:WAL方式通用性更强,适合几乎所有外部系统,但也不能提供百分百端到端的Exactly-Once;如果外部系统自身就支持事务(比如Kafka),可以使用2PC方式,提供百分百端到端的Exactly-Once。我们将在接下来的文章中详细介绍这两种方式。

WAL方式通用性更强, 不能提供百分百端到端的Exactly-Once。当仅仅当发生网络故障, fink应用提交,服务端已接收处理。但网络发生异常。。。fink认为错误,则会再次提交。

主要参考

Flink中文文档
干货 | Spark Streaming 和 Flink 详细对比
Flink Checkpoint机制原理剖析与参数配置
Flink如何保证端到端的Exactly-Once一致性


版权声明:本文为y3over原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。