一、高可用性

1. 高可用性定义

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。

2.高可用性计算指标

假设系统一直能够提供服务,我们说系统的可用性是100%。
如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。
很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时。

3.flink on yarn 实现高可用性

(1)Flink on Yarn定义

在运行高可用性YARN群集时,我们不会运行多个JobManager(ApplicationMaster)实例,而只会运行一个,由YARN在失败时重新启动。具体操作会根据yarn的版本不同而不同

(2)Flink on Yarn的优势
相对于 Standalone 模式,在Yarn 模式下有以下几点好处:
1.资源按需使用,提高集群的资源利用率;
2.任务有优先级,根据优先级运行作业;
3.基于 Yarn 调度系统,能够自动化地处理各个角色的 Failover:
JobManager 进程和 TaskManager 进程都由 Yarn NodeManager 监控;
如果 JobManager 进程异常退出,则 Yarn ResourceManager 会重新调度 JobManager 到其他机器;
如果 TaskManager 进程异常退出,JobManager 会收到消息并重新向 Yarn ResourceManager 申请资源,重新启动 TaskManager。

(3) Per-Job模式

  • 特点:每次递交作业都需要申请一次资源。
  • 优点:作业运行完成,资源会立刻被释放,不会一直占用系统资源
  • 缺点:每次递交作业都需要申请资源,会影响执行效率,因为申请资源需要消耗时间 应用场景:适合作业比较少的场景,大作业的场景

    (4)flink在yarn上的恢复行为
    https://cloud.tencent.com/developer/article/1586186 Flink 的 YARN 客户端具有以下配置参数来控制容器故障时的行为方式。这些参数可以从 conf/flink-conf.yaml 中设置,或者在启动会话时使用-D参数设置
    如:
    yarn.reallocate-failed: 此参数控制Flink是否应重新分配失败的TaskManager容器。默认值:true
    yarn.maximum-failed-containers: ApplicationMaster 在YARN会话失败之前接受的最大失败容器数。默认值:最初请求的TaskManagers(-n)的数量。
    yarn.application-attempts:ApplicationMaster(+其TaskManager容器)尝试的数量。如果此值设置为1(默认值),则当Application master失败时,整个YARN会话将失败。较高的值指定YARN重新启动ApplicationMaster的次数。

二、一致性

1.一致性定义

一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。 同时,一致性也是指事务的基本特征或特性相同,其他特性或特征相类似 。
详细解释:https://cloud.tencent.com/developer/article/1520172

2.flink的状态一致性

(1)什么是状态一致性

  • 有状态的流处理,内部每个算子任务都可以有自己的状态。
  • 对于流处理内部来说,所谓的状态一致性,其实就是我们所说的计算结果要保证准确。
  • 一条数据不应该丢失,也不应该重复计算。
  • 在遇到故障的时候可以状态恢复,恢复以后重新计算,计算结果也是完全正确的。

(2)分类

  • AT-MOST-ONCE(最多一次):当故障发生的时候,什么都不干。就是说每条消息就只消费一次。
  • AT-LEAST-ONCE(至少一次):为了确保数据不丢失,确保每个时间都得到处理,一些时间可能会被处理多次。
  • EXACTLY-ONCE(精确一次):每个时间都精确处理一次。

(3)flink实现状态一致性(exactly-once)
一致性实现源码解读: https://zhuanlan.zhihu.com/p/397012497
- 内部保证——分布式快照(checkpoint)
https://zhuanlan.zhihu.com/p/266620519

1. 定义
分布式中的可靠性可理解为容错性,checkpoint机制就是flink可靠性的基石。 Flink采用基于 checkpoint 的分布式快照机制,能够保证作业出现 fail-over 后可以从最新的快照进行恢复,即分布式快照机制可以保证 Flink 系统内部的“精确一次”处理。
Flink 分布式快照的核心元素包括:
Barrier(数据栅栏):可以把 Barrier 简单地理解成一个标记,该标记是严格有序的,并且随着数据流往下流动。每个 Barrier 都带有自己的 ID,Barrier 极其轻量,并不会干扰正常的数据处理。
异步:每次在把快照存储到我们的状态后端时,如果是同步进行就会阻塞正常任务,从而引入延迟。因此 Flink 在做快照存储时,采用异步方式
增量:由于 checkpoint 是一个全局状态,用户保存的状态可能非常大,多数达 G 或者 T 级别,checkpoint 的创建会非常慢,而且执行时占用的资源也比较多,因此 Flink 提出了增量快照的概念。也就是说,每次进行的全量 checkpoint,是基于上次进行更新的。

2.流程
https://www.jianshu.com/p/4d31d6cddc99

  • 每个需要checkpoint的应用在启动时,Flink的JobManager为其创建一个 CheckpointCoordinator,CheckpointCoordinator全权负责本应用的快照制作。
  • JobManager端的 CheckPointCoordinator向 所有SourceTask发送CheckPointTrigger,Source Task会在数据流中安插CheckPoint barrier
  • 当task收到所有的barrier后,向自己的下游继续传递barrier,然后自身执行快照,并将自己的状态异步写入到持久化存储中。增量CheckPoint只是把最新的一部分更新写入到 外部存储;为了下游尽快做CheckPoint,所以会先发送barrier到下游,自身再同步进行快照
  • 当task完成备份后,会将备份数据的地址(state handle)通知给JobManager的CheckPointCoordinator。如果CheckPoint的持续时长超过 了CheckPoint设定的超时时间,CheckPointCoordinator 还没有收集完所有的 State Handle,CheckPointCoordinator就会认为本次CheckPoint失败,会把这次CheckPoint产生的所有 状态数据全部删除。
  • 下游算子收到barrier之后,在exactly-once的前提下,会进行barrier对齐后,向下传递barrier,然后将自身的相关状态制作成快照,并保存到指定的持久化存储中,最后向CheckpointCoordinator报告自身 快照情况,恢复数据处理。
  • 每个算子按照上一步不断制作快照并向下游广播,直到最后barrier传递到sink算子,快照制作完成。
  • 最后 CheckPoint Coordinator 会把整个 StateHandle 封装成 completed CheckPoint Meta,写入到hdfs。

    **- source端一致性 **
    以下内容适用于 Flink 1.4 及之后版本
    kafka consumer 作为 source,可以将偏移量保存下来,当发生故障时可以从发生故障前的偏移量重新消费数据,从而保证一致性。
    flink kafka consumer :

- sink端一致性——两阶段提交
Sink 端是最复杂的,因为数据是落地到其他系统上的,数据一旦离开 Flink 之后,Flink 就监控不到这些数据了,所以精准一次处理语义必须也要应用于 Flink 写入数据的外部系统,故这些外部系统必须提供一种手段允许提交或回滚这些写入操作,同时还要保证与 Flink Checkpoint 能够协调使用(Kafka 0.11 版本已经实现精确一次处理语义)。

我们以 Flink 与 Kafka 组合为例,Flink 从 Kafka 中读数据,处理完的数据在写入 Kafka 中。
为什么以Kafka为例,第一个原因是目前大多数的 Flink 系统读写数据都是与 Kafka 系统进行的。第二个原因,也是最重要的原因 Kafka 0.11 版本正式发布了对于事务的支持,这是与Kafka交互的Flink应用要实现端到端精准一次语义的必要条件。
kafka producer 作为 sink,采用两阶段提交 sink,需要实现一个 TwoPhaseCOmmitSinkFunction
https://www.hnbian.cn/posts/6adf75db.html 1.sink收到第一条数据之后,开启一个 kafka 的事务( transaction),这时把数据写入kafka 标记为未提交, 这就是“预提交”;
2. jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到barrier 的算子将状态存入状态后端,并通知 jobmanager
3. sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知jobmanager,并开启下一阶段的事务,用于提交下个检查点的数据
4. jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成
5. sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据
6. 外部 kafka 关闭事务,提交的数据可以正常消费了。
flink 重启任务需要重新提交 flink重启后重复消费_flink
kafka事务性:https://zhuanlan.zhihu.com/p/120796378

3.flink多source的数据倾斜问题处理

单source,加random值,在进行hash计算后,进行keyby聚合
多source的话,为保证原有source之间的耦合性,可以挑选一个source加random值,其他source的不同key分别加固定的值

4.flink checkpoint失败后如何保证数据不重复消费

可以设置从上一个chekcpoint的状态恢复,然后消费

三、flink重复消费kafka数据问题

https://guosmilesmile.github.io/2020/09/07/FlinkKafkaConsumer%E9%87%8D%E5%A4%8D%E6%B6%88%E8%B4%B9%E6%95%B0%E6%8D%AE%E9%97%AE%E9%A2%98/

四、数据不丢失

https://support.huaweicloud.com/intl/zh-cn/dli_faq/dli_03_0096.html

五、flink实现有序性——watermark

https://cloud.tencent.com/developer/news/841119
1. 定义
Watermark 是在 Source function 处或之后立即生成的。Source function 的每个并行子任务通常独立地生成 Watermark。这些 Watermark 定义了该特定并行源的事件时间。
事件时间小于 t 但是晚于 Watermark(t) 到达。实际运行过程中,事件可能被延迟任意的时间,所以不可能指定一个时间,保证该时间之前的所有事件都被处理了。而且,即使延时时间是有界限的,过多的延迟的时间也是不理想的,会造成时间窗口处理的太多延时。
系统允许设置一个可容忍的延迟时间,在距离 t 的时间在可容忍的延迟时间内,可以继续处理数据,否则丢弃。

2. Kafka Connector 应用 WatermarkStrategy
每个 Kafka 分区一个时间戳
当使用 Kafka 作为数据源的时候,每个分区可能有一个简单的事件时间模式(按时间戳升序或其他)。当消费来自 Kafka 的流数据时,多个分区一般会并行消费。分区中的事件交替消费,会破坏分区中的模式。

在这种情况下,可以使用 Flink 的 Kafka-partition-aware(分区感知)watermark 生成器。使用这个特性的时候,watermark 会在 Kafka 消费者内部为每个分区生成,并且每个分区 watermark 的合并方式与在流进行 shuffle 时合并的方式相同。
3.窗口
http://wuchong.me/blog/2016/05/25/flink-internals-window-mechanism/

六、parallel 并行度的区别

1.并行度介绍

parallelism 是指 taskmanager 实际使用的并发能力。在 Flink 里面代表每个任务的并行度,适当的提高并行度可以大大提高 job 的执行效率,比如你的 job 消费 kafka 数据过慢,适当调大可能就消费正常了。
parallelism.default:1参数可以配置算子的并行度,没有指定的话,默认为1。不同算子可以单独设置并行度。

2.slot介绍

https://www.jianshu.com/p/b58988bcfb48slot 是指 taskmanager 可提供的并发执行能力。 taskmanager.numberOfTaskSlots:3参数可以配置每个taskmanager的slot数
Task Manager 是从 Job Manager 处接收需要部署的 Task,任务的并行性由每个 Task Manager 上可用的 slot 决定。每个 Flink TaskManager 在集群中提供 slot。 slot 的数量通常与每个 TaskManager 的可用 CPU 内核数成比例。一般情况下slot 数是你每个 TaskManager 的 cpu 的核数。
如果 Task Manager 有四个 slot,那么它将为每个 slot 分配 25% 的内存。 可以在一个 slot 中运行一个或多个线程。 同一 slot 中的线程共享相同的 JVM。 同一 JVM 中的任务共享 TCP 连接和心跳消息。Task Manager 的一个 Slot 代表一个可用线程,该线程具有固定的内存,注意 Slot 只对内存隔离,没有对 CPU 隔离。默认情况下,Flink 允许子任务共享 Slot,即使它们是不同 task 的 subtask,只要它们来自相同的 job。这种共享可以有更好的资源利用率。
flink 重启任务需要重新提交 flink重启后重复消费_kafka_02
上面图片中有两个 Task Manager,每个 Task Manager 有三个 slot,这样我们的算子最大并行度那么就可以达到 6 个,在同一个 slot 里面可以执行 1 至多个子任务。因此,上图中source/map/keyby/window/apply 最大可以有 6 个并行度,sink 只用了 1 个并行。

2.parallelism的设置方法

· 在flink的配置文件中flink-conf.yaml,默认的并行度为1;
· 在以shell的方式提交flink job的时候,可以使用-p指定程序的并行度;

./bin/flink run -p 10 ../word-count.jar

· 在flink job程序内设置并行度(这样设置的是所有算子的并行度,不能算子粒度设置)

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(10);

· 每个算子指定并行度 并行度设置优先级是:算子设置并行度 > env 设置并行度 > 配置文件默认并行度

data.keyBy(new xxxKey())
    .flatMap(new XxxFlatMapFunction()).setParallelism(5)
    .map(new XxxMapFunction).setParallelism(5)
    .addSink(new XxxSink()).setParallelism(1)

3.parallelism设置的注意事项

https://cloud.tencent.com/developer/article/1613761 Apache Flink的并行度设置并不是说越大越好、数据处理的效率就越高。而是需要设置合理的并行度。那么何谓合理呢?
Apache Flink的 并行度取决于每个TaskManager上的slot数量而决定的。Flink的JobManager把任务分成子任务提交给slot进行执行。相同的slot共享相同的JVM资源,同时对Flink提供维护的心跳等信息。
slot是指TaskManagere的并发执行能力,通常来说TaskManager有多少核CPU也就会有多少个slot。这样来看,我们设置的并行度其实是与TaskManager所有Slot数量有关的。

七、state介绍

https://www.pianshen.com/article/67611720660/

1.flink的有状态计算

程序计算过程中,在程序内部产生的中间结果,并提供给后续的算子。每个模块把自己的结果传递给下面的Task,也就是状态计算。

2.flink的state划分

https://zhuanlan.zhihu.com/p/104171679

  • Managed State 托管状态
    Managed State是由Flink管理的,Flink帮忙存储、恢复和优化。对Managed State继续细分,它又有两种类型:Keyed State和Operator State
    · Keyed State:与key相关,作用于key对应的Function或者Operator上。是KeyedStream上的状态。假如输入流按照id为Key进行了keyBy分组,形成一个KeyedStream,数据流中所有id为1的数据共享一个状态,可以访问和更新这个状态,以此类推,每个Key对应一个自己的状态。下图展示了Keyed State,因为一个算子子任务可以处理一到多个Key,算子子任务1处理了两种Key,两种Key分别对应自己的状态。

实现方法有:ValueState,ListState,ReducingState,AggregatingState,MapState。

· Operator State:与并行的算子实例绑定。并且在并行度发生变化的时候(划分一个State),能够自动重新分配状态数据。Operator State可以用在所有算子上,每个算子子任务或者说每个算子实例共享一个状态,流入这个算子子任务的数据可以访问和更新这个状态。下图展示了Operator State,算子子任务1上的所有数据可以共享第一个Operator State,以此类推,每个算子子任务上的数据共享自己的状态。

flink 重启任务需要重新提交 flink重启后重复消费_flink 重启任务需要重新提交_03

实现方法有:ListState,BroadcastState。

  • Raw State 原生状态
    Raw State是开发者自己管理的,需要自己序列化。适用于在已有算子和Managed State不够用时,用户自定义算子时使用。

3.state的存储

  • state的三种backend
  • MemoryStateBackend将工作state保存在TaskManager的内存中,并将checkpoint数据存储在JobManager的内存中。存在java的堆中。后端是轻量级的,没有其他依赖关系,但是可用性不高并且仅支持小状态。
  • FsStateBackend将工作state保存在TaskManager的内存中,并将checkpoint数据存储在文件系统中。
  • RocksDBStateBackend将工作state保存在RocksDB中,并且默认将checkpoint数据存在文件系统中,类似FsStateBackend。

八、flink反压

https://developer.aliyun.com/article/727389

1. 什么是反压

flink的反压在实时流计算中较为常见。指的是数据管道中某个节点成为瓶颈,处理速率跟不上上游发送数据的速率,而需要对上游进行限速。
基于 kafka+flink的架构而言,flink的数据源采用的是pull-based模式,因此flink的反压通常是从某个节点传导至数据源,并降低数据源(如kafka-consumer)的摄入速率

2.反压的影响

首先短时间的反压不会直接影响作业的可用性,只是潜在的性能瓶颈可能会导致更大的数据延迟。通常来说,对于一些数据量小或者延迟要求不是太高的应用而言,反压的影响可能并不明显,然而对于规模比较大的flink作业来说,反压可能会导致严重的问题。影响面主要有两点:
1. checkpint时长
反压出现时,相关数据流阻塞,会使数据管道中数据处理速度变慢,按正常数据量间隔插入的barrier也会被阻塞,进而拉长checkpoint时间,可能导致checkpoint超时,甚至失败。
2. state大小
在对齐checkpoint场景中,算子接收多个管道输入,输入较快的管道数据state会被缓存起来,等待输入较慢的管道数据barrier对齐,这样由于输入较快管道数据没被处理,一直积压可能导致OOM或者内存资源耗尽的不稳定问题。

3.反压定位

常用的两种方式用于定位反压,都要求排查时从source到sink整条流逐一排查节点,直到定位反压的问题根源。

1. 通过 Flink Web UI 自带的反压监控面板

Flink Web UI 的反压监控提供了 SubTask 级别的反压监控,原理是通过周期性对 Task 线程的栈信息采样,得到线程被阻塞在请求 Buffer(意味着被下游队列阻塞)的频率来判断该节点是否处于反压状态。默认配置下,这个频率在 0.1 以下则为 OK,0.1 至 0.5 为 LOW,而超过 0.5 则为 HIGH。

flink 重启任务需要重新提交 flink重启后重复消费_flink_04


如果处于反压状态,那么有两种可能性:

  1. 该节点向下发送数据的速率跟不上接收数据的速率,这种一般会发生在一条输入多条输出的operator(如flatmap);
  2. 下游节点接收数据的速率较慢,通过反压机制限制了该节点的发送速率。
    分析:如果是第一种情况,那么反压的根源节点即为该节点,它是从 Source Task 到 Sink Task 的第一个出现反压的节点。反之需向下继续排查。

注意:反压的根源节点并不一定会在反压面板体现出高反压,因为反压面板监控的是发送端,如果某个节点是性能瓶颈并不会导致它本身出现高反压,而是导致它的上游出现高反压。总体来看,如果我们找到第一个出现反压的节点,那么反压根源要么是就这个节点,要么是它紧接着的下游节点。这两种情况区分需要结合metrics来判断。

2. 通过 Flink Task Metrics

4.反压的原因及处理

我们主要观察Task Thread来定位原因,常见的反压原因及处理方法如下:

  1. 在实践中,很多情况下的反压是由于数据倾斜造成的。这点我们可以通过 Web UI 各个 SubTask 的 Records Sent 和 Record Received 来确认,另外 Checkpoint detail 里不同 SubTask 的 State size 也是一个分析数据倾斜的有用指标。
  2. 最常见的问题可能是用户代码的执行效率问题(频繁被阻塞或者性能问题)。最有用的办法就是对 TaskManager 进行 CPU profile,从中我们可以分析到 Task Thread 是否跑满一个 CPU 核:如果是的话要分析 CPU 主要花费在哪些函数里面,比如我们生产环境中就偶尔遇到卡在 Regex 的用户函数(ReDoS);如果不是的话要看 Task Thread 阻塞在哪里,可能是用户函数本身有些同步的调用,可能是 checkpoint 或者 GC 等系统活动导致的暂时系统暂停。
  3. TaskManager 的内存以及 GC 问题也可能会导致反压。包括 TaskManager JVM 各区内存不合理导致的频繁 Full GC 甚至失联。推荐可以通过给 TaskManager 启用 G1 垃圾回收器来优化 GC,并加上 -XX:+PrintGCDetails 来打印 GC 日志的方式来观察 GC 的问题。

5.案例示例及分析

https://zhuanlan.zhihu.com/p/269610943

九、精华

https://bbs.huaweicloud.com/blogs/310613

flink 重启任务需要重新提交 flink重启后重复消费_flink 重启任务需要重新提交_05