[Flink 基礎]-- 端到端的精準一次語義實現

感謝英文原文:https://flink.apache.org/features/2018/03/01/end-to-end-exactly-once-apache-flink.html

Apache Flink中的端到端精確一次處理概述(和Apache Kafka一樣)

2018年3月1日Piotr Nowojski(@PiotrNowojski)和Mike Winters(@wints

本文改編自2010年Flink Forward BerlinPiotr Nowojski的演講。你可以在Flink Forward Berlin網站上找到幻燈片和演示文稿。

2017年12月發佈的Apache Flink 1.4.0爲Flink引入了一個重要的流程處理里程碑:一個名爲TwoPhaseCommitSinkFunction相關的Jira 在這裏)的新功能,它提取了兩階段提交協議的通用邏輯,並且可以構建結束使用Flink和一系列數據源和接收器(包括Apache Kafka版本0.11及更高版本)完成一次應用程序。它提供了一個抽象層,並要求用戶只實現少數幾個方法來實現端到端的一次性語義。

如果需要了解所有內容,請告訴我們Flink文檔中的相關位置,您可以在其中閱讀有關如何投入TwoPhaseCommitSinkFunction使用的信息。

如果你想了解更多信息,我們將在這篇文章中分享對新功能的深入概述以及Flink幕後發生的事情。

在本文的其它部分,我們將:

  • 描述Flink檢查點在Flink應用程序中保證一次性結果的作用。
  • 顯示Flink如何通過兩階段提交協議與數據源和數據接收器交互,以提供 end-to-end  的一次性保證。
  • 詳細介紹如何使用 TwoPhaseCommitSinkFunction 實現一次性文件接收器的簡單示例。

當我們說 “exactly-once semantics” 時,我們的意思是每個傳入事件隻影響最終結果一次。即使機器或軟件出現故障,也沒有重複數據,也沒有未經處理的數據。

Flink長期以來 Flink應用程序中提供了一次性語義。在過去幾年中,已經深入探討了Flink的檢查點,這是Flink提供精確一次語義的能力的核心。Flink文檔還提供了該功能的全面概述

在繼續之前,這裏是檢查點算法的快速摘要,因爲理解檢查點對於理解這個更廣泛的主題是必要的。

Flink中的檢查點是一致性的快照:

  1. 應用程序的當前狀態
  2. 輸入流中的位置

Flink以固定的可配置間隔生成檢查點,然後將檢查點寫入持久存儲系統,例如S3或HDFS。將檢查點數據寫入持久存儲是異步發生的,這意味着Flink應用程序在檢查點過程中繼續處理數據。

如果發生機器或軟件故障,重新啓動後,Flink應用程序將從最近成功完成的檢查點恢復處理; Flink恢復應用程序狀態,並在再次開始處理之前從檢查點回滾到輸入流中的正確位置。這意味着Flink計算結果,就好像從未發生過失敗一樣。

在Flink 1.4.0之前,一次性語義僅限於Flink應用程序的範圍,並且沒有擴展到Flink在處理後發送數據的大多數外部系統。

但是Flink應用程序與各種數據接收器一起運行,並且開發人員應該能夠在一個組件的上下文之外保持一次性語義。

提供 end-to-end 的一次性語義 - 即除了Flink應用程序的狀態之外,還應用於Flink寫入的外部系統的語義 - 這些外部系統必須提供提交或回滾寫入的方法與Flink的檢查點協調。

協調分佈式系統中的提交和回滾的一種常用方法是兩階段提交協議。在下一節中,我們將進入幕後討論Flink如何TwoPhaseCommitSinkFunction 利用兩階段提交協議提供端到端的一次性語義。

我們將介紹兩階段提交協議以及它如何在一個讀取和寫入Kafka的示例Flink應用程序中實現端到端的一次性語義。Kafka是一個與Flink一起使用的流行消息傳遞系統,Kafka最近通過其0.11版本添加了對事務的支持。這意味着Flink現在擁有必要的機制,可以在從Kafka接收數據和向Kafka寫入數據時在應用程序中提供端到端的一次性語義

Flink對端到端一次性語義的支持不僅限於Kafka,您可以將它與任何提供必要協調機制的源/接收器一起使用。例如,來自Dell / EMC的開源流媒體存儲系統Pravega也支持Flink通過端口進行端到端的一次性語義 TwoPhaseCommitSinkFunction

一個示例Flink應用程序

現在我們要討論的示例Flink應用程序有:

要使數據接收器提供一次性保證,它必須在事務範圍內將所有數據寫入Kafka。提交捆綁了兩個檢查點之間的所有寫入。

這可確保在發生故障時回滾寫入。

但是,在具有多個併發運行的接收器任務的分佈式系統中,簡單的提交或回滾是不夠的,因爲所有組件必須在提交或回滾時“一致”以確保一致的結果。Flink使用兩階段提交協議及其預提交階段來解決這一挑戰。

檢查點的啓動表示我們的兩階段提交協議的“預提交”階段。當檢查點啓動時,Flink JobManager會將檢查點屏障(將數據流中的記錄分隔爲進入當前檢查點的集合與進入下一個檢查點的集合)注入數據流。

barrier 從 operator 傳遞給 operator 。對於每個operator ,它會觸發 operator 的狀態後端以獲取其狀態的快照。

Flink應用程序示例 - 預先提交

數據源存儲其Kafka偏移量,在完成此操作後,它將檢查點屏障傳遞給下一個 operator 。

如果 operator 具有內部狀態,則此方法有效。內部狀態是由Flink的狀態後端存儲和管理的所有內容 - 例如,第二個 operator 中的窗口總和。當進程只有內部狀態時,除了在檢查點之前更新狀態後端中的數據之外,不需要在預提交期間執行任何其他操作。Flink負責在檢查點成功的情況下正確提交這些寫入,或者在發生故障時中止它們。

示例Flink應用程序 - 預先設置沒有外部狀態

但是,當進程具有外部狀態時,必須稍微處理此狀態。外部狀態通常以寫入外部系統(如Kafka)的形式出現。在這種情況下,爲了提供一次性保證,外部系統必須爲與兩階段提交協議集成的事務提供支持。

我們知道我們示例中的數據接收器具有這樣的外部狀態,因爲它正在向Kafka寫入數據。在這種情況下,在預提交階段,除了將其狀態寫入狀態後端之外,數據接收器還必須預先提交其外部事務。

Flink應用程序示例 - 預先提交外部狀態

當檢查點 barrier 通過所有 operator 並且觸發的快照回調完成時,預提交階段結束。此時檢查點已成功完成,並且包含整個應用程序的狀態,包括預先提交的外部狀態。如果發生故障,我們將從此檢查點重新初始化應用程序。

下一步是通知所有 operator 檢查點已成功。這是兩階段提交協議的提交階段,JobManager爲應用程序中的每個 operator 發出檢查點完成的回調。數據源和窗口 operator 沒有外部狀態,因此在提交階段,這些 operator 不必執行任何操作。但是,數據接收器確實具有外部狀態,並使用外部寫入提交事務。

示例Flink應用程序 - 提交外部狀態

所以讓我們把所有這些不同的部分組合在一起:

  • 一旦所有 operator 完成預提交,他們就會發出提交。
  • 如果至少一個預提交失敗,則所有其他提交都將中止,然後我們回滾到上一個成功完成的檢查點。
  • 在成功預先提交之後,必須保證提交最終成功 - 我們的 operators 和我們的外部系統都需要做出這種保證。如果提交失敗(例如,由於間歇性網絡問題),整個Flink應用程序將失敗,根據用戶的重新啓動策略重新啓動,並且還有另一次提交嘗試。此過程至關重要,因爲如果提交最終未成功,則會發生數據丟失。

因此,我們可以確定所有 operators  都同意檢查點的最終結果:所有 operators  都同意數據已提交或提交被中止並回滾。

將兩階段提交協議放在一起所需的所有邏輯可能有點複雜,這就是爲什麼Flink將兩階段提交協議的通用邏輯提取到抽象TwoPhaseCommitSinkFunction 類中的原因.

我們將討論如何擴展 TwoPhaseCommitSinkFunction 基於文件的簡單示例。我們只需要實現四種方法,併爲完全一次的文件接收器提供它們的實現:

  1. beginTransaction - 爲了開始事務,我們在目標文件系統的臨時目錄中創建一個臨時文件。隨後,我們可以在處理數據時將數據寫入此文件。
  2. preCommit - 在預提交時,我們刷新文件,關閉它,再也不要寫入它。我們還將爲屬於下一個檢查點的任何後續寫入啓動新事務。
  3. commit - 在提交時,我們將預先提交的文件原子地移動到實際的目標目錄。請注意,這會增加輸出數據可見性的延遲。
  4. abort - 在中止時,我們刪除臨時文件。

我們知道,如果發生任何故障,Flink會將應用程序的狀態恢復到最新的成功檢查點。一個潛在的問題是在極少數情況下,在成功預先提交之後但在通知該事實(提交)到達  operators  之前發生故障。在這種情況下,Flink將 operators 恢復到已經預先提交但尚未提交的狀態。

我們必須在檢查點狀態下保存有關預提交事務的足夠信息,以便能夠重啓abortcommit重啓後的事務。在我們的示例中,這將是臨時文件和目標目錄的路徑。

TwoPhaseCommitSinkFunction採用此方案考慮在內,它總是發出從檢查點恢復狀態時先發制人的承諾。我們有責任以冪等的方式實現提交。一般來說,這應該不是問題。在我們的示例中,我們可以識別出這樣的情況:臨時文件不在臨時目錄中,但已經移動到目標目錄。

還有一些其他邊緣情況TwoPhaseCommitSinkFunction也會考慮在內。在Flink文檔中瞭解更多信息

Wrapping Up

如果已經做到這一點,感謝通過詳細的帖子與我們在一起。以下是我們涉及的一些要點:

  • Flink的檢查點系統作爲Flink的基礎,支持兩階段提交協議並提供端到端的一次性語義。
  • 這種方法的一個優點是Flink不像其他一些系統那樣實現傳輸中的數據 - 不需要像大多數批處理那樣將計算的每個階段寫入磁盤。
  • Flink的新功能TwoPhaseCommitSinkFunction提取了兩階段提交協議的通用邏輯,並使用Flink和支持事務的外部系統構建端到端的一次性應用程序成爲可能
  • Flink 1.4.0開始,Pravega和Kafka 0.11生成器都提供了一次性語義; Kafka首次在Kafka 0.11中引入了事務,這使得Kafka在Flink中成爲可能的 producer 。
  • kafka 0.11 producer 是在頂部實現TwoPhaseCommitSinkFunction,它比一次在-至少 kafka producer operator 提供了非常低的開銷。

我們對這個新功能的實現感到非常興奮,我們期待能夠TwoPhaseCommitSinkFunction在未來爲其他 producer 提供支持。

這篇文章首次出現在Artisans數據博客上,並由原作者Piotr Nowojski和Mike Winters貢獻給Apache Flink和Flink博客。

 

參考

https://www.ververica.com/blog/end-to-end-exactly-once-processing-apache-flink-apache-kafka

https://www.cnblogs.com/huxi2b/p/8459342.html

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章