1 回顾

上一篇文章对Flink中的核心概念之Time和Window设计之因做了深入的探讨。先来回顾一下:

EventTime是具有反演特性的现代流式计算建模的核心,在如何避免传输延时带来的错窗问题上,Flink使用watermark和Lateness机制进行处理,使得整个基础更加稳固。同时提供丰富的Window生成和计算机制,使得编写流式计算应用变得简单。

成熟的计算系统还必须具备健壮性,才能承担起更重要的业务应用。Flink中的窗口计算大多需要依赖历史计算结果,这种计算称之为有状态的。在分布式系统中,状态是引起复杂性的根源之一。Flink如何保证有状态的计算足够健壮,将在本篇进行说明。

2 一致性分类

分布式系统引入状态后,就引入了一致性问题。流式计算系统的一致性主要表现在对计算结果的正确性级别。通常分为3种:at-most-once、at-least-once、exactly-once。

2.1 at-most-once

at-most-once级别提供的保证是最弱的,甚至是事件可以丢失,或者可以认为是没有保障的。最多一次指的就是事件被用于计算是最多一次的,计算系统发生故障或存储丢失后完全不用考虑。

2.2 at-least-once

at-least-once级别提供稍强一些的保证。在故障发生时可以达到事件不丢失,至少被纳入计算流程处理一次。但计算系统是不提供不超过一次的计算保证的。这在关键系统中需要外部应用通过幂等性等机制来补充更强的保证,但无疑会引起复杂性的增加。相对于at-most-once,这可以算是比较大的进步了,毕竟在外部系统配合下实现exactly-once变得可行,只是复杂性增加了而已。

2.3 exactly-once

exactly-once级别提供最好的保证。在故障发生时仍能做到事件仅被处理一次,保证了计算结果的正确性。而Flink就是提供exactly-once一致性级别计算引擎。另外,Storm、Sparking Streaming也能够提供exactly-once一致性级别的保证,但是付出了性能方面的代价。这在Flink出现前,对于性能要求严苛的环境,要么通过复杂的缓存/计算架构来解决,要么在性能和一致性级别上做出牺牲。

3 Flink如何实现exactly-once

3.1 checkpoint

Flink的checkpoint设计可谓神来之笔。在事件定义清晰的情况下,引入ckpt事件,结合ckpt算子,在最细粒度进行状态的快照,实现了exactly-once。

同时,通过ckpt事件的调度,故障阈值的调优,还可以在快照灵敏度上、故障发现及时性方面做更细致的调优。插入ckpt事件的数据流如下所示。

[ Event, Event, ckpt, Event, ..., Event, ckpt, Event, ..., Event, ckpt, Event, ... ]

而在消费ckpt时,由特殊的算子进行快照即可。

3.2 故障确认

Flink通过ckpt进行状态快照,但有时临时的存储不可靠并不影响计算结果的正确性。Flink通过多次连续的状态快照失败来确认故障发生。

3.3 故障恢复

故障发生后,集群将调度新的可用资源进行计算任务的执行。这时最后一次ckpt触发的成功快照将会被用于新认为的定位。以及哪些事件需要反演重播。这将需要Source进行配合完成。同时,如果事件基于ProcessingTime/IngestTime来作为时间基准,对计算结果的正确性也存在影响。

3.4 savepoint

Flink除了自动插入ckpt特殊事件外,还会开放手动插入svpt的接口,使得外界可以对计算进行暂停/恢复控制。这仍然能够提供计算的exactly-once一致性保证。

savepoint通常用于系统升级的处理,在具备反演特性的EventTime时间基准下,整个计算任务将会变得可控、可升级,而且正确。

4 我们走到了哪里

本篇对Flink提供的可靠性及原理进行分析。

Flink对流式计算实现了exactly-once一致性级别保证。使用ckpt和svpt机制,实现了对计算任务的正确性和可控性。这些特性对于需要升级维护、关键场景的应用提供了很好的保证。

接下来将继续探讨Flink中的其他核心概念。