袋鼠雲:基於Flink構建實時計算平臺的總體架構和關鍵技術點

數棧DTinsight發表於2021-07-16

是雲原生—站式資料中臺PaaS,我們在github和gitee上有一個有趣的開源專案: , FlinkX是一個基於Flink的批流統一的資料同步工具,既可以採集靜態的資料,也可以採集實時變化的資料,是全域、異構、批流一體的資料同步引擎。大家喜歡的話請給我們點個 star!star!star!

github開源專案:

gitee開源專案:https://gitee.com/dtstack_dev_0/flinkx

 

平臺建設背景

傳統離線資料開發時效性較差,無法滿足快速迭代的網際網路需求。伴隨著以Flink為代表的實時技術的飛速發展,實時計算越來越多的被企業使用,但是在使用中下面提到的各種問題也隨之而來。開發者使用門檻高、產出的業務資料質量沒有保障、企業缺少統一平臺管理難以維護等。在諸多不利因素的影響下,我們決定利用現有的Flink技術構建一套完整的實時計算平臺。

平臺總體架構

從總體架構來看,實時計算平臺大體可以分為三層,計算平臺、排程平臺、資源平臺。每層承擔著相應的功能,同時層於層之間又有互動,符合高內聚、低耦合的設計原則,架構圖如下:

 

01

計算平臺

直接面向開發人員使用,可以根據業務接入各種外部資料來源,提供後續任務使用。資料來源配置完成後,就可以在上面做基於Flink框架視覺化的資料同步、sql化的資料計算的工作,並且可以對執行中的任務進行多維度的監控和告警。

02

排程平臺

該層接收到平臺傳過來的任務內容、配置後,接下來就是比較核心的工作,也是下文中重點展開的內容,這裡先做一個大體的介紹。根據任務型別的不同將使用不同的外掛進行解析。

  • 資料同步任務:接收到上層傳過來的json後,進入到FlinkX框架中,根據資料來源端和寫出目標端的不同生成對應的DataStream,最後轉換成JobGraph。
  • 資料計算任務:接收到上層傳過來的sql後,進入到FlinkStreamSql框架中,解析sql、註冊成表、生成transformation,最後轉換成JobGraph。

排程平臺將得到的JobGraph提交到對應的資源平臺,完成任務的提交。

03

資源平臺

目前可以對接多套不同的資源叢集,並且也可以對接不同的資源型別,如:yarn和k8s.

資料同步和資料計算

在排程平臺中,接收到使用者的任務後就開始了後面的一系列的轉換操作,最終讓任務執行起來。我們從底層的技術細節看看到底是如何基於Flink構建實時計算平臺,如何使用FlinkX、FlinkStreamSql做一站式開發。

01

FlinkX

作為資料處理的第一步,也是最基礎的一步,我們看看FlinkX是如何在Flink的基礎上做二次開發,使用使用者只需要關注同步任務的json指令碼和一些配置,無需關心呼叫Flink的細節,並支援下圖中的功能。

我們先看下Flink任務提交中涉及到流程,其中的互動流程圖如下:

那麼FlinkX又是如何在Flink的基礎對上述元件進行封裝和呼叫的,使得Flink作為資料同步工具使用更加簡單,主要從Client、JobManager、TaskManager三個部分進行擴充套件,涉及到的內容如下圖:

 

1、Client端:

FlinkX對原生的client做了部分定製化開發,在FlinkX-launcher模組下,主要有以下幾個步驟:

1)解析引數,如:並行度、savepoint路徑、程式的入口jar包(平常寫的Flink demo)、Flink-conf.yml中的配置等。

2)透過程式的入口jar包、外部傳入引數、savepoint引數生成PackagedProgram

3)透過反射呼叫PackagedProgram中指定的程式的入口jar包的main方法,在main方法中,透過使用者配置的reader和writer的不同,載入對應的外掛。

4)生成JobGraph,將其中需要的資源(Flink需要的jar包、reader和writer的jar包、Flink配置檔案等)加入到YarnClusterDescriptor的shipFiles中,最後YarnClusterDescriptor就可以和yarn互動啟動JobManager

5)任務提交成功後,Client端就可得到yarn返回的applicationId,後續既可以透過application跟蹤任務的狀態。

2、JobManager端:

client端提交完後,隨後yarn啟動jobmanager,jobmanager會啟動一些自己內部服務,並且會構建ExecutionGraph在這個過程中FlinkX主要做了以下兩件事:

1)不同外掛重寫InputFormat介面中的createInputSplits方法建立分片,在上游資料量較大或者需要多並行度讀取的時候,該方法就起到給每個並行度設定不同的分片作用。

比如:在兩個並行度讀取mysql時,透過配置的分片欄位(比如自增主鍵id)。

第一個並行度讀取sql為:select * from table where id mod 2=0;

第二個並行度讀取sql為:select * from table where id mod 2=1;

2)分片建立完後透過getInputSplitAssigner按順序返回分配給各個併發例項。

3、TaskManager端:

在TaskManager端接收到JobManager排程過來的task之後,就開始了自己的生命週期的呼叫,主要包含以下幾個重要的階段。

1)initialize-operator-states():迴圈遍歷該task所有的operator,並呼叫實現了CheckpointedFunction介面的 initializeState 方法,在FlinkX中為DtInputFormatSourceFunction和DtOutputFormatSinkFunction,該方法在任務第一次啟動的時候會被呼叫,作用是恢復狀態,當任務失敗時可以從最近一次checkpoint恢復讀取位置已經,從而可以達到續跑的目的,如下圖所示。

 

2)open-operators():該方法呼叫OperatorChain中所有StreamOperator的open方法,最後呼叫的是BaseRichInputFormat中的open方法。

該方法主要做以下幾件事

  • 初始化累加器,記錄讀入、寫出的條數、位元組數
  • 初始化自定義的Metric
  • 開啟限速器
  • 初始化狀態
  • 開啟讀取資料來源的連線(根據資料來源的不同,每個外掛各自實現)

3)run():呼叫InputFormat中的nextRecord方法、OutputFormat中的writeRecord方法進行資料的處理了資料處理。4)close-operators():做一些關閉操作,例如呼叫InputFormat、OutputFormat的 close 方法等,並做一些清理工作。以上就是TaskManager中StreamTask整體的生命流程,除了上面介紹的FlinkX是如何呼叫Flink介面,FlinkX還有如下一些特性。

4、FlinkX的特性

1)自定義累加器

累加器是從使用者函式和操作中,分散式地統計或者聚合資訊。每個並行例項建立並更新自己的Accumulator物件, 然後合併收集不同並行例項,在作業結束時由系統合併,並可將結果推動到普羅米修斯中,如圖:

 

2)支援離線和實時同步

我們知道FlinkX是一個支援離線和實時同步的框架,這裡以mysql資料來源為例,看看是如何實現的。

  • 離線任務:
    在DtInputFormatSourceFunction的run方法中會呼叫InputFormat的open方法讀取資料記錄到resultSet中,之後再呼叫reachedEnd方法的判斷resultSet的資料是否讀取完,如果讀取完,就走後續的close流程。

 

  • 實時任務:
    open方法和離線一致,在reachedEnd時判斷是否是輪詢任務,如果是則會進入到間隔輪詢的分支中,將上一次輪詢讀取到的最大的一個增量欄位值,作為本次輪詢開始位置進行下一次輪詢,輪詢流程圖如下:

 

3)髒資料管理和錯誤控制

是把寫入資料來源時出錯的資料記錄下來,並把錯誤原因分類,然後寫入配置的髒資料表。

錯誤原因目前有:型別轉換錯誤、空指標、主鍵衝突和其它錯誤四類。

錯誤控制是基於Flink的累加器,執行過程中記錄出錯的記錄數,然後在單獨的執行緒裡定時判斷錯誤的記錄數是否已經超出配置的最大值,如果超出,則丟擲異常使任務失敗。這樣可以對資料精確度要求不同的任務,做不同的錯誤控制,控制流程圖如下:

4)限速器

對於一些上游資料產生過快的任務,會對下游資料庫造成較大的壓力,故而需要在源端做一些速率控制,FlinkX使用的是令牌桶限流方式控制速率,如下圖。當源端產生資料的速率達到某個閾值時,就不會在讀取新的資料,在BaseRichInputFormat的open階段也初始化了限速器。

以上就是FlinkX資料同步的基本原理,但是資料業務場景中資料同步只是第一步,由於FlinkX目前的版本中只有ETL中的EL,並不具備對資料的轉換和計算的能力,故而需要將產生的資料流入到下游的FlinkStreamSql。

02

FlinkStreamSql

基於Flink,對其實時sql進行擴充套件,主要擴充套件了流與維表的join,並支援原生Flink SQL所有的語法,目前FlinkStreamSql source端只能對接kafka,所以預設上游資料來源都是kafka。

我們看看FlinkStreamSql 又是如何在Flink基礎之上做到使用者只需要關注業務sql程式碼,遮蔽底層是如何呼叫Flink api。整體流程和上面介紹的FlinkX基本類似,不同點在Client端,這裡主要包括sql解析、登錄檔、執行sql 三個部分,所以這裡重點介紹這部分。

1、解析SQL這裡主要是解析使用者寫的create function、create table、create view、insert into四種sql語句,封裝到結構化的SqlTree資料結構中,SqlTree中包含了自定義函式集合、外部資料來源表集合、檢視語句集合、寫資料語句集合。2、表註冊得到了上面解析的SqlTree之後,就可以將sql中create table語句對應的外部資料來源集合作為表註冊到tableEnv中,並且將使用者自定的udf註冊進tableEnv中。3、執行SQL將資料來源註冊成表之後,就可以執行後面的insert into的sql語句了,執行sql這裡會分兩種情況1)sql中沒有關聯維表,就直接執行sql

2)sql中關聯了維表,由於在Flink早期版本中不支援維表join語法,我們在這塊做了擴充套件,不過在FlinkStreamsql v1.11之後和社群保持了一致,支援了和維表join的語法。根據維表的型別不同,使用不同的關聯方式

  • 全量維表:將上游資料作為輸入,使用RichFlatMapFunction作為查詢運算元,初始化時將資料全表撈到記憶體中,然後和輸入資料組拼得到打寬後的資料,然後重新註冊一張大表,供後續sql使用。

 

  • 非同步維表:將上游資料作為輸入,使用RichAsyncFunction作為查詢運算元,並將查詢得到的資料使用LRU快取,然後和輸入資料組拼得到打寬後的資料,然後重新註冊一張大表,供後續sql使用。

上面介紹的就是和FlinkX在client端的不同之處,由於source端只有kafka且使用了社群原生的kafka-connector,所以在jobmanager端也沒有資料分片的邏輯,taskmanager邏輯和FlinkX基本類似,這裡不再介紹。

任務運維

當使用FlinkX和FlinkStreamSql開發完業務之後,接下來進入到了任務運維階段了,在運維階段,我們主要在任務執行資訊、資料進出指標metrics、資料延遲、反壓、資料傾斜等維度做了監控。

01

任務執行資訊

我們知道FlinkStreamSql是基於Flinksql封裝的,所以在提交任務執行時最終還是走的Flinksql的解析、驗證、邏輯計劃、邏輯計劃最佳化、物理計劃,最後將任務執行起來,也就得到了我們經常看見的DAG圖:

但是由於Flinksql對任務做了很多最佳化,以至於我們只能看到如上圖的大體DAG圖,子DAG圖裡面的一些細節我們是沒法直觀的看到發生了什麼事情。

所以我們在原來生成DAG圖的方式上進行了一定的改造,這樣就能直觀的看到子DAG圖中每個Operator和每個並行度裡面發生了什麼事情,有了詳細的DAG圖後其他的一些監控維度就能直觀的展示,比如:資料輸入輸出、延時、反壓、資料傾斜,在出現問題時就能具體定位到,如下圖的反壓:

瞭解了上面的結構後,我們看看是如何實現的。我們知道在client提交任務時,會生成JobGraph,JobGraph中的taskVertices集合就封裝了上圖完整的資訊,我們將taskVertices生成json後,然後在結合LatencyMarker和相關的metrics,在前端即可生成上圖,並做相應的告警。除了上面的DAG以外,還有自定義metrics、資料延時獲取等,這裡不具體介紹,有興趣的同學可以參考FlinkStreamSql專案。

使用案例

透過上面的介紹後,我們看下如何在平臺上使用,下面展示了一個完整的案例:使用FlinkX將mysql中新增使用者資料實時同步到kafka,然後使用Flinkstreamsql消費kafka實時計算每分鐘新增使用者數,產出結果落庫到下游mysql,供業務使用。

01

實時同步mysql新增資料

 

02

實時計算每分鐘新增使用者數

 

03

執行資訊

整體DAG,可以直觀的顯示上面提到的多項指標

解析後的詳細DAG圖,可以看到子DAG內部的多項指標

以上就是Flink在袋鼠雲實時計算平臺總體架構和一些關鍵的技術點,如有不足之處歡迎大家指出。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69995740/viewspace-2781745/,如需轉載,請註明出處,否則將追究法律責任。

相關文章