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 具有许多与协调 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。
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中
Flink 应用程序的作业可以被提交到长期运行的 Flink Session 集群、专用的 Flink Job 集群 或 Flink Application 集群。这些选项之间的差异主要与集群的生命周期和资源隔离保证有关。
三种方式明显的区别是,资源管理方的不一个。 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 的堆保存状态快照
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 而产生的状态。
Fink 根据 Barrier 提供精确一次的语义保证时需要进行对齐(Barrier alignment)实现了内部确保精确一次(exactly once)。你也可以根据实际情况进行配置可以产生以下结果:
为了实现整条链路的一致性,即端到端精确一次,你需要满足以下条件
对于sources的可重放,sink的幂等写,这很好理解。主要本段主要描述事务写。
简单概括来说,Flink的事务写(Transaction Write)是指,Flink先将待输出的数据保存下来暂时不向外部系统提交,等待Checkpoint结束的时刻,Flink上下游所有算子的数据都是一致时,将之前保存的数据全部提交(Commit)到外部系统。
如果使用事务写,那只把时间戳3之前的输出提交到外部系统,时间戳3以后的数据(例如时间戳5和8生成的数据)暂时保存下来,等待下次Checkpoint时一起写入到外部系统。这就避免了时间戳5这个数据产生多次结果,多次写入到外部系统。
在事务写的具体实现上,Flink目前提供了两种方式:
这两种方式也是很多数据库和分布式系统实现事务时经常采用的协议,Flink根据自身的场景对这两种协议做了适应性调整。这两种方式主要区别在于:WAL方式通用性更强,适合几乎所有外部系统,但也不能提供百分百端到端的Exactly-Once;如果外部系统自身就支持事务(比如Kafka),可以使用2PC方式,提供百分百端到端的Exactly-Once。我们将在接下来的文章中详细介绍这两种方式。
WAL方式通用性更强, 不能提供百分百端到端的Exactly-Once。当仅仅当发生网络故障, fink应用提交,服务端已接收处理。但网络发生异常。。。fink认为错误,则会再次提交。
《Flink中文文档》
《干货 | Spark Streaming 和 Flink 详细对比》
《Flink Checkpoint机制原理剖析与参数配置》
《Flink如何保证端到端的Exactly-Once一致性》