Apache Flink binding Kafka build end Exactly-Once Processing

Author: Piotr Nowojski
Translation | Zhou Kaibo
Alibaba technical expert, Master of Sichuan University, graduating in 2010后加入阿里search division, engaged in research and development platform for offline search, the search will participate in reconstruction of background data processing architecture of MapReduce to Flink. Currently Computing Platform Division at Ali, focused on the one-stop computing platform based on Flink's construction.


table of Contents:


1.Apache Flink application Exactly-Once semantics

2.Flink application Exactly-Once-end semantics

3. Sample Flink application starts pre-submission stage

4. Implement two-phase commit Operator in the Flink

5. Summary


Apache Flink since December 2017 released version 1.4.0 began, introduced an important milestone for the flow characteristic calculation: TwoPhaseCommitSinkFunction ( related Jira ). It extracts the general logic of the two-phase commit protocol, such that the program Exactly-Once constructed by end Flink possible. Some data support the source (source) and output terminal (sink), comprising Apache Kafka 0.11 and later. It provides a layer of abstraction, the user need only a few methods can be achieved from end to end semantics Exactly-Once.


See the documentation for the use of TwoPhaseCommitSinkFunction:  TwoPhaseCommitSinkFunction . Or it can directly read Kafka 0.11 sink documents:  Kafka used to live .


The next will be a detailed analysis of this new functionality and implementation logic Flink, divided into the following points.


  • Describe how Flink checkpoint mechanism is the guarantee Exactly-Once Flink results of the program

  • Flink commit protocol shows how the data source and data output by the two-stage interaction, providing guaranteed end-Exactly-Once

  • Through a simple example of how to use the file output TwoPhaseCommitSinkFunction achieve Exactly-Once the


A, Apache Flink application Exactly-Once semantics


When we say "Exactly-Once" refers to each event entered once only affect the final outcome. Even if the machine or software failure, neither duplicate data, it will not lose data.


Flink provides a semantic Exactly-Once a long time ago. In the past few years, we Flink of checkpoint mechanisms have been thoroughly described, it is Flink ability to provide core Exactly-Once semantics. Flink document also provides the functionality of a comprehensive overview .


Before continuing, look under the brief introduction to the checkpoint mechanism, which is on the back of understanding of the subject is essential.


A checkpoint is consistent snapshot of the following:

  • The current state of the application

  • The position of the input stream


Flink a fixed time point may be configured to periodically generate checkpoint, the checkpoint data is written to persistent storage system, e.g. S3 or HDFS. The checkpoint data is written to the persistent store occurs asynchronously, which means Flink application can continue processing data in the checkpoint process.


If the machine or software failure, restart, Flink application recovery process from the latest checkpoint point; Flink will restore application state, will enter the last checkpoint rolled back into the saved position, and then start running again. This means Flink can be like that never happened as the result of the calculation fault.


在Flink 1.4.0之前,Exactly-Once语义仅限于Flink应用程序内部,并没有扩展到Flink数据处理完后发送的大多数外部系统。Flink应用程序与各种数据输出端进行交互,开发人员需要有能力自己维护组件的上下文来保证Exactly-Once语义。


为了提供端到端的Exactly-Once语义 – 也就是说,除了Flink应用程序内部,Flink写入的外部系统也需要能满足Exactly-Once语义 – 这些外部系统必须提供提交或回滚的方法,然后通过Flink的checkpoint机制来协调。


分布式系统中,协调提交和回滚的常用方法是两阶段提交协议。在下一节中,我们将讨论Flink的TwoPhaseCommitSinkFunction是如何利用两阶段提交协议来提供端到端的Exactly-Once语义。


二、Flink应用程序端到端的Exactly-Once语义


我们将介绍两阶段提交协议,以及它如何在一个读写Kafka的Flink程序中实现端到端的Exactly-Once语义。Kafka是一个流行的消息中间件,经常与Flink一起使用。Kafka在最近的0.11版本中添加了对事务的支持。这意味着现在通过Flink读写Kafaka,并提供端到端的Exactly-Once语义有了必要的支持


Flink对端到端的Exactly-Once语义的支持不仅局限于Kafka,您可以将它与任何一个提供了必要的协调机制的源/输出端一起使用。例如Pravega,来自DELL/EMC的开源流媒体存储系统,通过Flink的TwoPhaseCommitSinkFunction也能支持端到端的Exactly-Once语义。


v2-c6700a374580b111adff80f4366fcc60_hd.jpg


在今天讨论的这个示例程序中,我们有:


– 从Kafka读取的数据源(Flink内置的KafkaConsumer

– 窗口聚合

– 将数据写回Kafka的数据输出端(Flink内置的KafkaProducer


要使数据输出端提供Exactly-Once保证,它必须将所有数据通过一个事务提交给Kafka。提交捆绑了两个checkpoint之间的所有要写入的数据。这可确保在发生故障时能回滚写入的数据。但是在分布式系统中,通常会有多个并发运行的写入任务的,简单的提交或回滚是不够的,因为所有组件必须在提交或回滚时“一致”才能确保一致的结果。Flink使用两阶段提交协议及预提交阶段来解决这个问题。


在checkpoint开始的时候,即两阶段提交协议的“预提交”阶段。当checkpoint开始时,Flink的JobManager会将checkpoint barrier(将数据流中的记录分为进入当前checkpoint与进入下一个checkpoint)注入数据流。


brarrier在operator之间传递。对于每一个operator,它触发operator的状态快照写入到state backend。


v2-1e7f07d22094fe36bcde1d258f813c63_hd.jpg


数据源保存了消费Kafka的偏移量(offset),之后将checkpoint barrier传递给下一个operator。


这种方式仅适用于operator具有『内部』状态。所谓内部状态,是指Flink state backend保存和管理的 -例如,第二个operator中window聚合算出来的sum值。当一个进程有它的内部状态的时候,除了在checkpoint之前需要将数据变更写入到state backend,不需要在预提交阶段执行任何其他操作。Flink负责在checkpoint成功的情况下正确提交这些写入,或者在出现故障时中止这些写入。


v2-1f7e403aba52e1a1cc963083d9127e98_hd.jpg


三、示例Flink应用程序启动预提交阶段


但是,当进程具有『外部』状态时,需要作些额外的处理。外部状态通常以写入外部系统(如Kafka)的形式出现。在这种情况下,为了提供Exactly-Once保证,外部系统必须支持事务,这样才能和两阶段提交协议集成。


在本文示例中的数据需要写入Kafka,因此数据输出端(Data Sink)有外部状态。在这种情况下,在预提交阶段,除了将其状态写入state backend之外,数据输出端还必须预先提交其外部事务。


v2-1d80a2976491e6fbed9ce749be3a8aa5_hd.jpg

当checkpoint barrier在所有operator都传递了一遍,并且触发的checkpoint回调成功完成时,预提交阶段就结束了。所有触发的状态快照都被视为该checkpoint的一部分。checkpoint是整个应用程序状态的快照,包括预先提交的外部状态。如果发生故障,我们可以回滚到上次成功完成快照的时间点。


下一步是通知所有operator,checkpoint已经成功了。这是两阶段提交协议的提交阶段,JobManager为应用程序中的每个operator发出checkpoint已完成的回调。


数据源和widnow operator没有外部状态,因此在提交阶段,这些operator不必执行任何操作。但是,数据输出端(Data Sink)拥有外部状态,此时应该提交外部事务。


v2-1b4310079e03df58ac2c66b0bc4a6045_hd.jpg



我们对上述知识点总结下:


– 一旦所有operator完成预提交,就提交一个commit。

– 如果至少有一个预提交失败,则所有其他提交都将中止,我们将回滚到上一个成功完成的checkpoint。

– 在预提交成功之后,提交的commit需要保证最终成功 – operator和外部系统都需要保障这点。如果commit失败(例如,由于间歇性网络问题),整个Flink应用程序将失败,应用程序将根据用户的重启策略重新启动,还会尝试再提交。这个过程至关重要,因为如果commit最终没有成功,将会导致数据丢失。


因此,我们可以确定所有operator都同意checkpoint的最终结果:所有operator都同意数据已提交,或提交被中止并回滚。


四、在Flink中实现两阶段提交Operator


完整的实现两阶段提交协议可能有点复杂,这就是为什么Flink将它的通用逻辑提取到抽象类TwoPhaseCommitSinkFunction中的原因。


接下来基于输出到文件的简单示例,说明如何使用TwoPhaseCommitSinkFunction。用户只需要实现四个函数,就能为数据输出端实现Exactly-Once语义:


– beginTransaction – 在事务开始前,我们在目标文件系统的临时目录中创建一个临时文件。随后,我们可以在处理数据时将数据写入此文件。

– preCommit – 在预提交阶段,我们刷新文件到存储,关闭文件,不再重新写入。我们还将为属于下一个checkpoint的任何后续文件写入启动一个新的事务。

– commit – 在提交阶段,我们将预提交阶段的文件原子地移动到真正的目标目录。需要注意的是,这会增加输出数据可见性的延迟。

– abort – 在中止阶段,我们删除临时文件。


我们知道,如果发生任何故障,Flink会将应用程序的状态恢复到最新的一次checkpoint点。一种极端的情况是,预提交成功了,但在这次commit的通知到达operator之前发生了故障。在这种情况下,Flink会将operator的状态恢复到已经预提交,但尚未真正提交的状态。


我们需要在预提交阶段保存足够多的信息到checkpoint状态中,以便在重启后能正确的中止或提交事务。在这个例子中,这些信息是临时文件和目标目录的路径。


TwoPhaseCommitSinkFunction已经把这种情况考虑在内了,并且在从checkpoint点恢复状态时,会优先发出一个commit。我们需要以幂等方式实现提交,一般来说,这并不难。在这个示例中,我们可以识别出这样的情况:临时文件不在临时目录中,但已经移动到目标目录了。


在TwoPhaseCommitSinkFunction中,还有一些其他边界情况也会考虑在内,请参考Flink文档了解更多信息。


总结


总结下本文涉及的一些要点:


  • Flink的checkpoint机制是支持两阶段提交协议并提供端到端的Exactly-Once语义的基础。

  • 这个方案的优点是: Flink不像其他一些系统那样,通过网络传输存储数据 – 不需要像大多数批处理程序那样将计算的每个阶段写入磁盘。

  • Flink的TwoPhaseCommitSinkFunction提取了两阶段提交协议的通用逻辑,基于此将Flink和支持事务的外部系统结合,构建端到端的Exactly-Once成为可能。

  • Flink 1.4.0开始,Pravega和Kafka 0.11 producer都提供了Exactly-Once语义;Kafka在0.11版本首次引入了事务,为在Flink程序中使用Kafka producer提供Exactly-Once语义提供了可能性。

  • Kafaka 0.11 producer的事务是在TwoPhaseCommitSinkFunction基础上实现的,和at-least-once producer相比只增加了非常低的开销。


这是个令人兴奋的功能,期待Flink TwoPhaseCommitSinkFunction在未来支持更多的数据接收端。


Guess you like

Origin blog.51cto.com/14286418/2402144