flink进阶篇-09-状态编程和容错机制

流式计算分为无状态有状态两种情况。

1)无状态的计算观察每个独立事件,并根据最后一个事件输出结果。例如,流处理应用程序从传感器接收温度读

数,并在温度超过 90 度时发出警告。

2)有状态的计算则会基于多个事件输出结果。

以下是一些例子。

⚫ 所有类型的窗口。例如,计算过去一小时的平均温度,就是有状态的计算。

⚫ 所有用于复杂事件处理的状态机。例如,若在一分钟内收到两个相差 20 度以上的温度读数,则发出警告,这

是有状态的计算。

⚫ 流与流之间的所有关联操作,以及流与静态表或动态表之间的关联操作,都是有状态的计算。

下图展示了无状态流处理和有状态流处理的主要区别。无状态流处理分别接收每条数据记录(图中的黑条),然

后根据最新输入的数据生成输出数据(白条)。

有状态流处理会维护状态(根据每条输入记录进行更新),并基于最新输入的记录和当前的状态值生成输出记录

(灰条)。

flink进阶篇-09-状态编程和容错机制_第1张图片

 

上图中输入数据由黑条表示。无状态流处理每次只转换一条输入记录,并且仅根据最新的输入记录输出结果

(白条)。有状态流处理维护所有已处理记录的状态值,并根据每条新输入的记录更新状态,因此输出记录(灰条)反

映的是综合考虑多个事件之后的结果。

尽管无状态的计算很重要,但是流处理对有状态的计算更感兴趣。事实上,正确地实现有状态的计算比实现

无状态的计算难得多。旧的流处理系统并不支持有状态的计算,而新一代的流处理系统则将状态及其正确性

视为重中之重

1、有状态的算子和应用程序

Flink 内置的很多算子,数据源 source,数据存储 sink 都是有状态的,流中的数据都是 buffer records

会保存一定的元素或者元数据。例如: ProcessWindowFunction 会缓存输入流的数据,ProcessFunction 会保

存设置的定时器信息等等。

在 Flink 中,状态始终与特定算子相关联。总的来说,有两种类型的状态:

⚫ 算子状态(operator state)

⚫ 键控状态(keyed state)

1.1 算子状态(operator state)

算子状态的作用范围限定为算子任务。这意味着由同一并行任务所处理的所有数据都可以访问到相同的状

状态对于同一任务而言是共享的。

算子状态不能由相同或不同算子的另一个任务访问。

flink进阶篇-09-状态编程和容错机制_第2张图片

 

Flink 为算子状态提供三种基本数据结构:

⚫ 列表状态(List state)

将状态表示为一组数据的列表。

⚫ 联合列表状态(Union list state)

也将状态表示为数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启

动应用程序时如何恢复。

⚫ 广播状态(Broadcast state)

如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态。

1.2 键控状态(keyed state)

键控状态是根据输入数据流中定义的键(key)来维护和访问的。Flink 为每个键值维护一个状态实例,并将

具有相同键的所有数据,都分区到同一个算子任务中,这个任务会维护和处理这个 key 对应的状态。当任务处理

一条数据时,它会自动将状态的访问范围限定为当前数据的 key。因此,具有相同 key 的所有数据都会访问相同的

状态。

Keyed State 很类似于一个分布式的 key-value map 数据结构,只能用于 KeyedStream(keyBy 算子处理

之后)。

flink进阶篇-09-状态编程和容错机制_第3张图片

 

Flink 的 Keyed State 支持以下数据类型:

ValueState保存单个的值,值的类型为 T。

get 操作: ValueState.value()

set 操作: ValueState.update(T value)

ListState保存一个列表,列表里的元素的数据类型为 T。基本操作如下:

ListState.add(T value)

ListState.addAll(List values)

ListState.get()返回 Iterable

ListState.update(List values)

MapState保存 Key-Value 对。

MapState.get(UK key)

MapState.put(UK key, UV value)

MapState.contains(UK key)

MapState.remove(UK key)

ReducingState

AggregatingState

State.clear()是清空操作。

我们可以利用 Keyed State,实现这样一个需求:

检测传感器的温度值,如果连续的两个温度差值超过 10 度,就输出报警。

DataStream> warningStream = dataStream
 .keyBy("id")
 .flatMap(new TempIncreaseWarning(10.0));

这里需要实现一个自定义的 RichFlatMapFuction,具体实现如下:

public static class TempIncreaseWarning extends RichFlatMapFunction>{
    private Double threshold;
    TempIncreaseWarning(Double threshold) {
        this.threshold = threshold;
    }
    private ValueState lastTempState;
    
    @Override
    public void open(Configuration parameters) throws Exception {
        lastTempState = getRuntimeContext().getState(new 
        ValueStateDescriptor("last-temp", Double.class, Double.MIN_VALUE));
    }
 
    @Override
    public void flatMap(SensorReading value, Collector> 
out) throws Exception {
        Double lastTemp = lastTempState.value();
        lastTempState.update(value.getTemperature());
        if( lastTemp != Double.MIN_VALUE ) {// 跟最新的温度值计算差值,如果大于阈值,那么输出报警
            Double diff = Math.abs(value.getTemperature() - lastTemp);
            if (diff > threshold)
                out.collect( new Tuple3<>(value.getId(), lastTemp, 
                value.getTemperature()) );
        }
    }
}

➢ 通过 RuntimeContext 注册 StateDescriptor

➢ StateDescriptor 以状态 state 的名字和存储的数据类型为参数。

➢ 在 open()方法中创建 state 变量。注意复习之前的 RichFunction 相关知识。

2、状态一致性

当在分布式系统中引入状态时,自然也引入了一致性问题。一致性实际上是"正确性级别"的另一种说法,也就

是说在成功处理故障并恢复之后得到的结果,与没有发生任何故障时得到的结果相比,前者到底有多正确?举例来

说,假设要对最近一小时登录的用户计数。在系统经历故障之后,计数结果是多少?如果有偏差,是有漏掉的计数

还是重复计数?

在flink中对有状态的流处理做了以下特点说明:

1)有状态的流处理,内部每个[算子]任务都可以有自己的状态。

2)对于流处理器内部来说,所谓的状态一致性,其实就是我们所说的计算结果要保证准确。

3)一条数据不应该丢失,也不应该重复计算。

4) 在遇到故障时可以恢复状态,恢复以后的重新计算,结果应该也是完全正确的。

如图:数据重做后,仍然要保持状态的一致性

flink进阶篇-09-状态编程和容错机制_第4张图片

 

2.1 一致性级别

在流处理中,一致性可以分为 3 个级别:

at-most-once (最多一次)

当任务故障时,最简单的做法是什么都不干,既不恢复丢失的状态,也不重播丢失的数据。 这其实是没有正

确性保障的委婉说法——故障发生之后,计数结果可能丢失。

at-least-once (至少一次)

在大多数的真实应用场景,我们希望不丢失事件。这种类型的保障称为 at-least-once,意思是所有的事件都

得到了处理,而一些事件还可能被处理多次。 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是

说,计数程序在发生故障后可能多算,但是绝不会少算。

exactly-once (精确一次)

恰好处理一次是最严格的保证,也是最难实现的。恰好处理一次语义不仅仅意味着没有事件丢失,还意味着

针对每一个数据,内部状态仅仅更新一次。

曾经,at-least-once 非常流行。第一代流处理器(如 Storm 和 Samza)刚问世时只保证 at-least-once,原因有二:

⚫ 保证 exactly-once 的系统实现起来更复杂。这在基础架构层(决定什么代表正确,以及 exactly-once 的范围是什么)和实现层都很有挑战性。

⚫ 流处理系统的早期用户愿意接受框架的局限性,并在应用层想办法弥补(例如使应用程序具有幂等性,或者用批量计算层再做一遍计算)。

最先保证 exactly-once 的系统(Storm TridentSpark Streaming)在性能和表现力这两个方面付出了很

大的代价。为了保证 exactly-once,这些系统无法单独地对每条记录运用应用逻辑,而是同时处理多条(一批)记

录,保证对每一批的处理要么全部成功,要么全部失败。这就导致在得到结果前,必须等待一批记录处理结束。因

此,用户经常不得不使用两个流处理框架(一个用来保证 exactly-once,另一个用来对每个元素做低延迟处理),

结果使基础设施更加复杂。

曾经,用户不得不在保证 exactly-once 与获得低延迟和效率之间权衡利弊。Flink 避免了这种权衡。 Flink 的

一个重大价值在于,它既保证了 exactly-once,也具有低延迟高吞吐的处理能力。

从根本上说,Flink 通过使自身满足所有需求来避免权衡,它是业界的一次意义重大的技术飞跃。尽管这在外

行看来很神奇,但是一旦了解,就会恍然大悟。

2.2 端到端(end-to-end)状态一致性

目前我们看到的一致性保证都是由流处理器实现的,也就是说都是在 Flink 流处理器内部保证的;而在真实应

用中,流处理应用除了流处理器以外还包含了数据源(例如 Kafka)和输出到持久化系统。

端到端的一致性保证,意味着结果的正确性贯穿了整个流处理应用的始终;每一个组件都保证了它自己的一

致性,整个端到端的一致性级别取决于所有组件中一致性最弱的组件。具体可以划分如下:

内部保证 —— 依赖 checkpoint

source 端 —— 需要外部源可重设数据的读取位置

sink 端 —— 需要保证从故障恢复时,数据不会重复写入外部系统

而对于 sink 端,又有两种具体的实现方式:幂等(Idempotent)写入事务性(Transactional)写入

⚫ 幂等写入

所谓幂等操作,是说一个操作,可以重复执行很多次,但只导致一次结果更改, 也就是说,后面再重复执行

就不起作用了。

flink进阶篇-09-状态编程和容错机制_第5张图片

 

⚫ 事务写入

需要构建事务来写入外部系统,构建的事务对应着 checkpoint,等到 checkpoint 真正完成的时候,才把所

有对应的结果写入 sink 系统中。

对于事务性写入,具体又有两种实现方式:预写日志(WAL)两阶段提交(2PC)。DataStream API 提供

GenericWriteAheadSink 模板类和 TwoPhaseCommitSinkFunction 接口,可以方便地实现这两种方式的

事务性写入。

1) 预写日志(Write-Ahead-Log,WAL)

➢ 把结果数据先当成状态保存,然后在收到 checkpoint 完成的通知时, 一次性写入 sink 系统

➢ 简单易于实现,由于数据提前在状态后端中做了缓存,所以无论什么sink 系统,都能用这种方式一批搞定

➢ DataStream API 提供了一个模板类:GenericWriteAheadSink,来实现这种事务性 sink

2) 两阶段提交(Two-Phase-Commit,2PC)

➢ 对于每个 checkpoint,sink 任务会启动一个事务,并将接下来所有接收的数据添加到事务里

➢ 然后将这些数据写入外部 sink 系统,但不提交它们 —— 这时只是“预提交”

➢ 当它收到 checkpoint 完成的通知时,它才正式提交事务,实现结果的真正写入

2PC这种方式真正实现了exactly-once,它需要一个提供事务支持的外部 sink 系统。Flink 提供了

TwoPhaseCommitSinkFunction 接口。

2.3 2PC 对外部 sink 系统的要求

1)外部 sink 系统必须提供事务支持,或者 sink 任务必须能够模拟外部系统上的事务

2)在 checkpoint 的间隔期间里,必须能够开启一个事务并接受数据写入

3)在收到 checkpoint 完成的通知之前,事务必须是“等待提交”的状态。 在故障恢复的情况下,这可能需要一些时间。如果这个时候sink系统关闭事务(例如超时了),那么未提交的数据就会丢失

4)sink 任务必须能够在进程失败后恢复事务

5)提交事务必须是幂等操作

2.4 Source 和 Sink 的一致性说明

不同 Source 和 Sink 的一致性保证可以用下表说明:

flink进阶篇-09-状态编程和容错机制_第6张图片

 

3、检查点(checkpoint)

Flink 具体如何保证 exactly-once 呢? 它使用一种 种轻量级快照机制 被称为"检查点"(checkpoint)的特

性,在出现故障时将系统重置回正确状态。下面通过简单的类比来解释检查点的作用。

假设你和两位朋友正在数项链上有多少颗珠子,如下图所示。你捏住珠子,边数边拨,每拨过一颗珠子就给

总数加一。你的朋友也这样数他们手中的珠子。当你分神忘记数到哪里时,怎么办呢? 如果项链上有很多珠子,你

显然不想从头再数一遍,尤其是当三人的速度不一样却又试图合作的时候,更是如此(比如想记录前一分钟三人一

共数了多少颗珠子,回想一下一分钟滚动窗口)。

flink进阶篇-09-状态编程和容错机制_第7张图片

 

于是,你想了一个更好的办法:在项链上每隔一段就松松地系上一根有色皮筋, 将珠子分隔开; 当珠子被拨动

的时候,皮筋也可以被拨动; 然后,你安排一个助手,让他在你和朋友拨到皮筋时记录总数。用这种方法,当有人

数错时,就不必从头开 始数。相反,你向其他人发出错误警示,然后你们都从上一根皮筋处开始重数,助 手则会

告诉每个人重数时的起始数值,例如在粉色皮筋处的数值是多少。

Flink 检查点的作用就类似于皮筋标记。数珠子这个类比的关键点是: 对于指定的皮筋而言,珠子的相对位置

是确定的; 这让皮筋成为重新计数的参考点。总状态(珠子的总数)在每颗珠子被拨动之后更新一次,助手则会保存

与每根皮筋对应的检查点状态,如当遇到粉色皮筋时一共数了多少珠子,当遇到橙色皮筋时又是多少。 当问题出

现时,这种方法使得重新计数变得简单。

总结:

状态流应用的一致检查点,其实就是:

1)所有任务的状态,在某个时间点的一份拷贝一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候。

有状态流应用的一致检查点,其实就是:所有任务的状态,在某个时间点的一份拷贝(一份快照)。而这个时间点,应该是所有任务都恰好处理完一个相同的输入数据的时候。

2)应用状态的一致检查点,是 Flink 故障恢复机制的核心

flink进阶篇-09-状态编程和容错机制_第8张图片

 

3.1 Flink 的检查点算法

Flink 检查点的核心作用是确保状态正确,即使遇到程序中断,也要正确。记住这一基本点之后,我们用一个

例子来看检查点是如何运行的。Flink 为用户提供了用来定义状态的工具。

下图表示程序的初始状态: 输入流中的 6 条记录被检查点分割线(checkpoint barrier)隔开,所有的 map 算

子状态均为 0(计数还未开始),按key分组,数据采用累加方式进行最后输出。

所有 key 为 a 的记录将被顶层的 map 算子处理,所有 key 为 b的记录将被中间层的 map 算子处理,所有

key 为 c 的记录则将被底层的 map 算子处理。

flink进阶篇-09-状态编程和容错机制_第9张图片

 

上图是程序的初始状态。注意,a、b、c 三组的初始计数状态都是 0,即三个圆柱上的值。ckpt 表示检查点

分割线(checkpoint barriers)。每条记录在处理顺序上严格地遵守在检查点之前或之后的规定,例如

["b",2]在检查点之前被处理,["a",2]则在检查点之后被处理。

当该程序处理输入流中的 6 条记录时,涉及的操作遍布 3 个并行实例(节点、CPU内核等)。那么,检查点该如

何保证 exactly-once 呢?

检查点分割线和普通数据记录类似。它们由算子处理,但并不参与计算,而是会触发与检查点相关的行为。

当读取输入流的数据源(在本例中与 keyBy 算子内联) 遇到检查点屏障时,它将其在输入流中的位置保存到持久化

存储中。

如果输入流来自消息传输系统(Kafka),这个位置就是偏移量。Flink 的存储机制是插件化的,持久化存储可以是分布式文件系统,如 HDFS。下图展示了这个过程。

flink进阶篇-09-状态编程和容错机制_第10张图片

 

当 Flink 数据源(在本例中与 keyBy 算子内联)遇到检查点分界线(barrier)时, 它会将其在输入流中的位置

保存到持久化存储中。这让 Flink 可以根据该位置重启。

检查点像普通数据记录一样在算子之间流动。当 map 算子处理完前 3 条数据并收到检查点分界线时,它们会

将状态以异步的方式写入持久化存储,如下图所示。

flink进阶篇-09-状态编程和容错机制_第11张图片

 

位于检查点之前的所有记录(["b",2]、["b",3]和["c",1])被 map 算子处理之后的情 况。

此时,持久化存储已经备份了检查点分界线在输入流中的位置(备份操作发生在barrier 被输入算子处理的时

候)。map 算子接着开始处理检查点分界线,并触发将状 态异步备份到稳定存储中这个动作。

当 map 算子的状态备份和检查点分界线的位置备份被确认之后,该检查点操作就可以被标记为完成,如下图

所示。

我们在无须停止或者阻断计算的条件下,在一个逻辑时间点(对应检查点屏障在输入流中的位置)为计算状态拍

了快照。通过确保备份的状态和位置指向同一个逻辑时间点,后文将解释如何基于备份恢复计算,从 而保证

exactly-once。值得注意的是,当没有出现故障时,Flink 检查点的开销极小, 检查点操作的速度由持久化存储的

可用带宽决定。

回顾数珠子的例子: 除了因为数错而需要用到皮筋之外,皮筋会被很快地拨过。

flink进阶篇-09-状态编程和容错机制_第12张图片

 

检查点操作完成,状态和位置均已备份到稳定存储中。

输入流中的所有数据记录都已处理完成。值得注意的是,备份的状态值与实际的状态值是不同的。备份反

映的是检查点的状态。

如果检查点操作失败,Flink 可以丢弃该检查点并继续正常执行,因为之后的某一个检查点可能会成功。虽然

恢复时间可能更长,但是对于状态的保证依旧很有力。只有在一系列连续的检查点操作失败之后,Flink 才会抛出

错误,因为这通常预示着发生了严重且持久的错误。

现在来看看下图所示的情况: 检查点操作已经完成,但故障紧随其后。

flink进阶篇-09-状态编程和容错机制_第13张图片

 

在这种情况下,Flink 会重新拓扑(可能会获取新的执行资源),将输入流倒回到上一个检查点,然后恢复状态

并从该处开始继续计算。在本例中,["a",2]、["a",2] 和["c",2]这几条记录将被重播。

下图展示了这一重新处理过程。从上一个检查点开始重新计算,可以保证在剩下的记录被处理之后,得到的

map 算子的状态值与没有发生故障时的状态值一致。

flink进阶篇-09-状态编程和容错机制_第14张图片

 

Flink 将输入流倒回到上一个检查点屏障的位置,同时恢复 map 算子的状态值。然后,Flink 从此处开始重新

处理。这样做保证了在记录被处理之后,map 算子的状态值与没有发生故障时的一致。

Flink 检查点算法的正式名称是异步分界线快照(asynchronous barrier snapshotting)。该算法大致基于

Chandy-Lamport 分布式快照算法。

检查点是 Flink 最有价值的创新之一,因为它使 Flink 可以保证 exactly-once, 并且不需要牺牲性能。

3.2 恢复状态流程

3.1示例中讲到异常处理步骤,如何进行重置,我们再来聊聊。

1)重启应用

遇到故障之后,第一步就是重启应用

flink进阶篇-09-状态编程和容错机制_第15张图片

 

2)状态重置

⚫ 从 checkpoint 中读取状态,将状态重置

⚫ 从检查点重新启动应用程序后,其内部状态与检查点完成时的状态完全相同

flink进阶篇-09-状态编程和容错机制_第16张图片

 

3)数据恢复

⚫ 开始消费并处理检查点到发生故障之间的所有数据

⚫ 这种检查点的保存和恢复机制可以为应用程序状态提供“精确一次” (exactly-once)的一致性,因为所有

算子都会保存检查点并恢复其所有状 态,这样一来所有的输入流就都会被重置到检查点完成时的位置

flink进阶篇-09-状态编程和容错机制_第17张图片

 

3.3 检查点分界线(Checkpoint Barrier)

1)Flink 的检查点算法用到了一种称为分界线(barrier)的特殊数据形式,用来把一条流上数据按照不同的检查

点分开

2)分界线之前到来的数据导致的状态更改,都会被包含在当前分界线所属 的检查点中;而基于分界线之后的数据

导致的所有更改,就会被包含在 之后的检查点中

图例:

A)有两个输入流的应用程序,用并行的两个 Source 任务来读取

flink进阶篇-09-状态编程和容错机制_第18张图片

 

B)JobManager 会向每个 source 任务发送一条带有新检查点 ID 的消息,通过这 种方式来启动检查点

flink进阶篇-09-状态编程和容错机制_第19张图片

 

C)数据源将它们的状态写入检查点,并发出一个检查点 barrier

D)状态后端在状态存入检查点之后,会返回通知给 source 任务,source 任务就会向 JobManager 确认检查点完成

flink进阶篇-09-状态编程和容错机制_第20张图片

 

3.4 分界线对齐流程(barrier)

1)开始对齐

• 分界线对齐:barrier 向下游传递,sum 任务会等待所有输入分区的 barrier 到达

• 对于barrier已经到达的分区,继续到达的数据会被缓存

• 而barrier尚未到达的分区,数据会被正常处理

flink进阶篇-09-状态编程和容错机制_第21张图片

 

2)结束对齐

当收到所有输入分区的 barrier 时,任务就将其状态保存到状态后端的检查点中, 然后将 barrier 继续向下游转发

flink进阶篇-09-状态编程和容错机制_第22张图片

 

3)下游继续传递

向下游转发检查点 barrier 后,任务继续正常的数据处理

flink进阶篇-09-状态编程和容错机制_第23张图片

 

4)Sink确认

• Sink 任务向 JobManager 确认状态保存到 checkpoint 完毕

• 当所有任务都确认已成功将状态保存到检查点时,检查点就真正完成了

flink进阶篇-09-状态编程和容错机制_第24张图片

 

3.5 Flink+Kafka 如何实现端到端的 exactly-once 语义

我们知道,端到端的状态一致性的实现,需要每一个组件都实现,对于 Flink + Kafka 的数据管道系统

(Kafka 进、Kafka 出)而言,各组件怎样保证 exactly-once 语义呢?

内部 —— 利用 checkpoint 机制,把状态存盘,发生故障的时候可以恢复, 保证内部的状态一致性

source —— kafka consumer 作为 source,可以将偏移量保存下来,如果后续任务出现了故障,恢复的时候

可以由连接器重置偏移量,重新消费数据, 保证一致性

sink —— kafka producer 作为 sink,采用两阶段提交 sink,需要实现一个TwoPhaseCommitSinkFunction

内部的 checkpoint 机制我们已经有了了解,那 source 和 sink 具体又是怎样运行的呢?接下来我们逐步做一

个分析。

我们知道 Flink 由 JobManager 协调各个 TaskManager 进行 checkpoint 存储, checkpoint 保存在

StateBackend 中,默认 StateBackend 是内存级的,也可以改为文件级的进行持久化保存。

具体的两阶段提交步骤总结如flink进阶篇-09-状态编程和容错机制_第25张图片下:

flink进阶篇-09-状态编程和容错机制_第26张图片 

flink进阶篇-09-状态编程和容错机制_第27张图片 

flink进阶篇-09-状态编程和容错机制_第28张图片 

flink进阶篇-09-状态编程和容错机制_第29张图片 

 

⚫ 第一条数据来了之后,开启一个 kafka 的事务(transaction),正常写入kafka 分区日志但标记为未提交,这

就是“预提交

⚫ jobmanager 触发 checkpoint 操作,barrier 从 source 开始向下传递,遇到barrier 的算子将状态存入状态后

端,并通知 jobmanager

⚫ sink 连接器收到 barrier,保存当前状态,存入 checkpoint,通知 jobmanager,并开启下一阶段的事务,用

于提交下个检查点的数据

⚫ jobmanager 收到所有任务的通知,发出确认信息,表示 checkpoint 完成

⚫ sink 任务收到 jobmanager 的确认信息,正式提交这段时间的数据

⚫ 外部 kafka 关闭事务,提交的数据可以正常消费了。

所以我们也可以看到,如果宕机需要通过 StateBackend 进行恢复,只能恢复所有确认提交的操作。

4、保存点(save points)

1)Flink 还提供了可以自定义的镜像保存功能,就是保存点(savepoints)

2)原则上,创建保存点使用的算法与检查点完全相同,因此保存点可以认为就是具有一些额外元数据的检查点

3)Flink不会自动创建保存点,因此用户(或者外部调度程序)必须明确地触发创建操作

4)保存点是一个强大的功能。除了故障恢复外,保存点可以用于:有计划 的手动备份,更新应用程序,版本迁移,暂停和重启应用,等等

5、状态后端(state backend)

⚫ MemoryStateBackend

内存级的状态后端,会将键控状态作为内存中的对象进行管理,将它们存储在 TaskManager 的 JVM 堆上;

而将 checkpoint 存储在 JobManager 的内存中。

⚫ FsStateBackend

将 checkpoint 存到远程的持久化文件系统(FileSystem)上。而对于本地状态,跟 MemoryStateBackend

一样,也会存在 TaskManager 的 JVM 堆上。

⚫ RocksDBStateBackend

将所有状态序列化后,存入本地的 RocksDB 中存储。

注意:RocksDB 的支持并不直接包含在 flink 中,需要引入依赖:


 org.apache.flink
 flink-statebackend-rocksdb_2.12
 1.10.1

设置状态后端为 FsStateBackend,并配置检查点和重启策略:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 1. 状态后端配置
env.setStateBackend(new FsStateBackend(""));
// 2. 检查点配置
env.enableCheckpointing(1000);
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
env.getCheckpointConfig().setCheckpointTimeout(60000);
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
env.getCheckpointConfig().setPreferCheckpointForRecovery(false);
env.getCheckpointConfig().setTolerableCheckpointFailureNumber(0);
// 3. 重启策略配置
// 固定延迟重启(隔一段时间尝试重启一次)
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
 3, // 尝试重启次数
 100000 // 尝试重启的时间间隔,也可 org.apache.flink.api.common.time.Time
));

你可能感兴趣的:(大数据,flink)