Apache Flink 在汽車之家的應用與實踐

ApacheFlink發表於2021-11-05

本文整理自汽車之家實時計算平臺負責人邸星星在 Flink Forward Asia 2020 分享的議題《Apache Flink 在汽車之家的應用及實踐》。主要內容包括:

  1. 背景及現狀
  2. AutoStream 平臺
  3. 基於 Flink 的實時生態建設
  4. 後續規劃

img

一、背景及現狀

1. 第一階段

在 2019 年之前,汽車之家的大部分實時業務都是執行在 Storm 之上的。Storm 作為早期主流的實時計算引擎,憑藉簡單的 Spout 和 Bolt 程式設計模型以及叢集本身的穩定性,俘獲了大批使用者,我們在 2016 年搭建了 Storm 平臺。

圖片

隨著實時計算的需求日漸增多,資料規模逐步增大,Storm 在開發及維護成本上都凸顯了不足,這裡列舉幾個痛點:

  1. 開發成本高

    我們一直是用的 Lambda 架構,會用 T+1 的離線資料修正實時資料,即最終以離線資料為準,所以計算口徑實時要和離線完全保持一致,實時資料開發的需求文件就是離線的 SQL,實時開發人員的核心工作就是把離線的 SQL 翻譯成 Storm 程式碼,期間雖然封裝了一些通用的 Bolt 來簡化開發,但把離線動輒幾百行的 SQL 精準地翻譯成程式碼還是很有挑戰的,並且每次執行都要經過打包、上傳、重啟的一系列的繁瑣操作,除錯成本很高。

  1. 計算低效

    Storm 對狀態支援的不好,通常需要藉助 Redis、HBase 這類 kv 儲存維護中間狀態,我們之前是強依賴 Redis。比如常見的計算 UV 的場景,最簡單的辦法是使用 Redis 的 sadd 命令判斷 uid 是否為已經存在,但這種方法會帶來很高的網路 IO,同時如果沒有提前報備的大促或搞活動導致流量翻倍的情況,很容易把 Redis 記憶體搞滿,運維同學也會被殺個措手不及。同時 Redis 的吞吐能力也限制了整個作業的吞吐量。

  1. 難以維護、管理

    由於採用編寫 Storm 程式碼方式開發,難以分析後設資料及血緣關係,同時可讀性差,計算口徑不透明,業務交接成本很高。

  1. 對數倉不友好

    資料倉儲團隊是直接對接業務需求的團隊,他們更熟悉基於 Hive 的 SQL 開發模式,通常都不擅長 Storm 作業的開發,這導致一些原本是實時的需求,只能退而求其次選擇 T+1 的方式給出資料。

在這個階段,我們支援了最基本的實時計算需求,因為開發門檻比較高,很多實時業務都是由我們平臺開發來完成,既做平臺,又做資料開發,精力分散很嚴重。

2. 第二階段

圖片

我們從 2018 年開始調研 Flink 引擎,其相對完備的 SQL 支援,天生對狀態的支援吸引了我們,在經過學習調研後,2019 年初開始設計開發 Flink SQL 平臺,並於 2019 年中上線了 AutoStream 1.0 平臺。平臺上線之初就在倉庫團隊、監控團隊和運維團隊得以應用,能夠快速被使用者主要得益於以下幾點:

  1. 開發、維護成本低:汽車之家大部分的實時任務可以用 Flink SQL + UDF 實現。平臺提供常用的 Source 和 Sink,以及業務開發常用的 UDF,同時使用者可以自己編寫 UDF。基於 "SQL + 配置" 的方式完成開發,可以滿足大部分需求。對於自定義任務,我們提供方便開發使用的 SDK,助力使用者快速開發自定義 Flink 任務。平臺面向的使用者已經不只是專業的資料開發人員了,普通開發、 測試、運維人員經過基本的學習都可以在平臺上完成日常的實時資料開發工作,實現平臺賦能化。資料資產可管理,SQL 語句本身是結構化的,我們通過解析一個作業的 SQL,結合 source、 sink 的 DDL,可以很容易的知道這個作業的上下游,天然保留血緣關係。
  1. 高效能:Flink 可以完全基於狀態 (記憶體,磁碟) 做計算,對比之前依賴外部儲存做計算的場景,效能提升巨。在 818 活動壓測期間,改造後的程式可以輕鬆支援原來幾十倍流量的實時計算,且橫向擴充套件效能十分良好。
  1. 全面的監控報警:使用者將任務託管在平臺上,任務的存續由平臺負責,使用者專注於任務本身的邏輯開發本身即可。對於 SQL 任務,SQL 的可讀性極高,便於維護;對於自定義任務,基於我們 SDK 開發,使用者可以更專注於梳理業務邏輯上。不論是 SQL 任務還是 SDK,我們都內嵌了大量監控,並與報警平臺關聯,方便使用者快速發現分析定位並修復任務,提高穩定性。
  1. 賦能業務:支援數倉分層模型,平臺提供了良好的 SQL 支援,數倉人員可以藉助 SQL,將離線數倉的建設經驗應用於實時數倉的建設上,自平臺上線後,數倉逐步開始對接實時計算需求。

圖片

痛點:

  1. 易用性有待提高,比如使用者無法自助管理 UDF,只能使用平臺內建的 UDF 或者把打好的 jar 包發給平臺管理員,通過人工的方式處理上傳問題。
  1. 隨著平臺作業量的高速增長,平臺 on-call 成本非常高。首先我們經常面對一些新使用者的基礎問題:

    1. 平臺的使用問題;
    2. 開發過程中遇到的問題,比如為什麼打包報錯;
    3. Flink UI 的使用問題;
    4. 監控圖形的含義,如何配置報警。

還有一些不太容易快速給出答案的問題:

  1. Jar 包衝突;
  2. 為什麼消費 Kafka 延遲;
  3. 任務為什麼報錯。

尤其是延遲問題,我們常見的資料傾斜,GC,反壓問題可以直接引導使用者去 Flink UI 和監控圖表上去檢視,但有時候還是需要手動去伺服器上檢視 jmap、jstack 等資訊,有時候還需要生成火焰圖來幫助使用者定位效能問題。

初期我們沒有和運營團隊合作,完全是我們開發人員直接對接處理這些問題,雖然期間補充了大量的文件,但是整體上 on-call 成本還是很高。

  1. 在 Kafka 或 Yarn 出現故障時,沒有快速恢復的方案,當面對一些重保業務時,有些捉襟見肘。眾所周知,沒有永遠穩定,不出故障的環境或元件,當有重大故障出現時,需要有快速恢復業務的應對方案。
  1. 資源沒有合理管控,存在比較嚴重的資源浪費的情況。隨著使用平臺開發任務的使用者不斷增加,平臺的作業數也不斷增加。有些使用者不能很好的把控叢集資源的使用,經常出現過多申請資源的問題,導致作業執行效率低下甚至空閒,造成了資源的浪費。

在 AutoStream1.0 平臺這個階段,基於 SQL 開發的方式極大地降低了實時開發的門檻,各業務方可以自己實現實時業務的開發,同時數倉同學經過簡單的學習後,就開始對接實時業務,將我們平臺方從大量的業務需求中釋放出來,讓我們可以專心做平臺方面的工作。

3. 當前階段

圖片

針對上面的幾個方面,我們有針對性行的做了以下幾點升級:

  1. 引入 Jar Service:支援使用者自助上傳 UDF jar 包,並在 SQL 片段中自助引用,實現自助管理 UDF。同時自定義作業也可以配置 Jar Service 中的 Jar,面對多個作業共用同一個 Jar 的場景,使用者只需要在作業中配置 Jar Service 中的 jar 包路徑就可以,避免每次上線都重複上傳 Jar 的繁瑣操作;
  2. 自助診斷:我們開發了動態調整日誌級別、自助檢視火焰圖等功能,方便使用者自己定位問題,減輕我們日常 on-call 成本;
  3. 作業健康檢查功能:從多個維度分析,為每個 Flink 作業打分,每個低分項都相應的給出建議;
  4. Flink 作業級別的快速容災恢復:我們建設了兩套 YARN 環境,每一個 YARN 對應一個單獨的 HDFS,兩個 HDFS 之前通過 SNAPSHOT 方式進行 Checkpoint 資料的雙向複製,同時在平臺上增加了切換叢集的功能,在一個 YARN 叢集不可用的情況下,使用者可以自助在平臺上,選擇備用叢集的 Checkpoint;
  5. Kafka 多叢集架構支援:使用我們自研的 Kafka SDK,支援快速切換 Kafka 叢集;
  6. 對接預算系統:每個作業佔用的資源都直接對應到預算團隊,這樣一定程度上保證資源不會被其他團隊佔用,同時每個團隊的預算管理員可以檢視預算使用明細,瞭解自己的預算支援了團隊內的哪些業務。

目前使用者對平臺的使用已經趨於熟悉,同時自助健康檢查和自助診斷等功能的上線,我們平臺方的日常 on-call 頻率在逐步降低,開始逐漸進入平臺建設的良性迴圈階段。

4. 應用場景

圖片

汽車之家用於做實時計算的資料主要分為三類:

  1. 客戶端日誌,也就是我們內部說的點選流日誌,包括使用者端上報的啟動日誌、時長日誌、PV 日誌、點選日誌以及各類事件日誌,這類主要是使用者行為日誌,是我們建設實時數倉中流量寬表、UAS 系統、實時畫像的基礎,在這之上還支援了智慧搜尋、智慧推薦等線上業務;同時基礎的流量資料也用於支援各業務線的流量分析、實時效果統計,支援日常運營決策。
  2. 服務端日誌,包括 nginx 日誌、各類後端應用產生的日誌、各種中介軟體的日誌。這些日誌資料主要用於後端服務的健康監測、效能監控等場景。
  3. 業務庫的實時變更記錄,主要有三種:MySQL 的 binlog,SQLServer 的 CDC,TiDB 的 TiCDC 資料,基於這些實時的資料變更記錄,我們通過對各種內容資料的抽象與規範,建設了內容中臺、資源池等基礎服務;也有一些做簡單邏輯的業務資料實時統計場景,結果資料用於實時大屏、羅盤等,做資料展現。

以上這三類資料都會實時寫入 Kafka 叢集,在 Flink 叢集中針對不同場景進行計算,結果資料寫入到 Redis、MySQL、Elasticsearch、HBase、Kafka、Kylin 等引擎中,用於支援上層應用。

下面列舉了一些應用場景:

圖片

5. 叢集規模

目前 Flink 叢集伺服器 400+,部署模式為 YARN (80%) 和 Kubernetes,執行作業數 800+,日計算量 1 萬億,峰值每秒處理資料 2000 萬條。

圖片

二、AutoStream 平臺

1. 平臺架構

圖片

上面是 AutoStream 平臺目前的整體架構,主要是以下幾部分內容:

  1. AutoStream core System

    這是我們平臺的核心服務,負責對後設資料服務、Flink 客戶端服務、Jar 管理服務及互動結果查詢服務進行整合,通過前端頁面把平臺功能暴露給使用者。

主要包括 SQL 和 Jar 作業的管理、庫表資訊的管理、UDF 管理、操作記錄及歷史版本的管理、健康檢查、自助診斷、報警管理等模組,同時提供對接外部系統的能力,支援其他系統通過介面方式管理庫表資訊、SQL 作業資訊及作業啟停操作等。基於 Akka 任務的生命週期管理和排程系統提供了高效,簡單,低延遲的操作保障,提升了使用者使用的效率和易用性。

  1. 後設資料服務 (Catalog-like Unified Metastore)

    主要對應 Flink Catalog 的後端實現,除了支援基本的庫表資訊管理外,還支援庫表粒度的許可權控制,結合我們自身的特點,支援使用者組級別的授權。

底層我們提供了 Plugin Catalog 機制,既可以用於和 Flink 已有的 Catalog 實現做整合,也可以方便我們嵌入自定義的 Catalogs,通過 Plugin 機制可以很容易的重用 HiveCatalog,JdbcCatalog 等,從而保證了庫表的週期的一致性。

同時後設資料服務還負責對使用者提交的 DML 語句進行解析,識別當前作業的依賴的表資訊,用於作業的分析及提交過程,同時可以記錄血緣關係。

  1. Jar Service

    平臺提供的各類 SDK 在 Jar Service 上進行統一管理,同時使用者也可以在平臺上把自定義 Jar、UDF jar 等提交到 Jar Service 上統一管理,然後在作業中通過配置或 DDL 引用。

  1. Flink 客戶端服務 (Customed Flink Job Client)

    負責把平臺上的作業轉化成 Flink Job 提交到 Yarn 或 Kubernetes 上,我們在這一層針對 Yarn 和 Kubernetes 做了抽象,統一兩種排程框架的行為,對外暴露統一介面及規範化的引數,弱化 Yarn 和 Kubernetes 的差異,為 Flink 作業在兩種框架上無縫切換打下了良好的基礎。

每個作業的依賴不盡相同,我們除了對基礎依賴的管理以外,還需要支援個性化的依賴。比如不同版本的 SQL SDK,使用者自助上傳的 Jar、UDF 等,所以不同作業的提交階段需要做隔離。

我們採用的是 Jar service + 程式隔離的方式,通過和 Jar Service 對接,根據作業的型別和配置,選用相應的 Jar,並且提交單獨的程式中執行,實現物理隔離。

  1. 結果快取服務 (Result Cache Serivce)

    是一個簡易的快取服務,用於 SQL 作業開發階段的線上除錯場景。當我們分析出使用者的 SQL 語句,將 Select 語句的結果集存入快取服務中;然後使用者可以在平臺上通過選擇 SQL 序號 (每個完整的 SELECT 語句對應一個序號),實時檢視 SQL 對應的結果資料,方便使用者開發與分析問題。

  1. 內建Connectors (Source & Sink)

    最右側的部分主要是各種 Source、Sink 的實現,有一些是重用 Flink 提供的 connector,有一些是我們自己開發的 connector。

針對每一種 connector 我們都新增了必要 Metric,並配置成單獨的監控圖表,方便使用者瞭解作業執行情況,同時也為定位問題提供資料依據。

2. 基於 SQL 的開發流程

在平臺提供以上功能的基礎上,使用者可以快速的實現 SQL 作業的開發:

  1. 建立一個 SQL 任務;
  2. 編寫 DDL 宣告 Source 和 Sink;
  3. 編寫 DML,完成主要業務邏輯的實現;
  4. 線上檢視結果,若資料符合預期,新增 INSERT INTO 語句,寫入到指定 Sink 中即可。

圖片

平臺預設會儲存 SQL 每一次的變更記錄,使用者可以線上檢視歷史版本,同時我們會記錄針對作業的各種操作,在作業維護階段可以幫助使用者追溯變更歷史,定位問題。

下面是一個 Demo,用於統計當天的 PV、UV 資料:

圖片

3. 基於 Catalog 的後設資料管理

圖片

後設資料管理的主要內容:

  1. 支援許可權控制:除了支援基本的庫表資訊管理外,還支援表粒度的許可權控制,結合我們自身的特點,支援使用者組級別的授權;
  2. Plugin Catalog 機制:可以組合多種其他 Catalog 實現,複用已有的 Catalog;
  3. 庫表生命週期行為統一:使用者可以選擇平臺上的表和底層儲存的生命週期統一,避免兩邊分別維護,重複建表;
  4. 新老版本完全相容:由於在 AutoStream 1.0 的時候,我們沒有單獨引入 Metastore 服務,此外 1.0 時期的 DDL SQL 解析模組是自研的元件。所以在建設 MetaStore 服務時,需要考慮歷史作業和歷史庫表資訊相容的問題。

    1. 對於庫表資訊,新的 MetaStore 在底層將新版和舊版的庫表資訊轉換成統一的儲存格式,從而保證了庫表資訊的相容性。
    2. 對於作業,這裡我們通過抽象介面,並分別提供 V1Service 和 V2Service 兩種實現路徑,保證了新老作業在使用者層面的相容。

下面是幾個模組和 Metastore 互動的示意圖:

圖片

4. UDXF 管理

我們引入了 Jar Service 服務用來管理各種 Jar,包括使用者自定義作業、平臺內部 SDK 元件、UDXF 等,在 Jar Service 基礎上我們可以很容易的實現 UDXF 的自助管理,在 On k8s 的場景下,我們提供了統一的映象,Pod 啟動後會從 Jar Service 下載對應的 Jar 到容器內部,用於支援作業的啟動。

使用者提交的 SQL 中如果包含 Function DDL,我們會在 Job Client Service 中會解析 DDL,下載對應的 Jar 到本地。

為了避免和其他作業有依賴衝突,我們每次都會單獨啟動一個子程式來完成作業提交的操作。UDXF Jar 會被並加入到 classpath 中,我們對 Flink 做了一些修改,作業提交時會把這個 Jar 一併上傳到 HDFS 中;同時 AutoSQL SDK 會根據函式名稱和類名為當前作業註冊 UDF。

圖片

5. 監控報警及日誌收集

得益於 Flink 完善的 Metric 機制,我們可以方便的新增 Metric,針對 Connector,我們內嵌了豐富的 Metric,並配置了預設的監控看板,通過看板可以檢視 CPU、記憶體、JVM、網路傳輸、Checkpoint、各種 Connector 的監控圖表。同時平臺和公司的雲監控系統對接,自動生成預設的報警策略,監控存活狀態、消費延遲等關鍵指標。同時使用者可以在雲監控系統修改預設的報警策略,新增新的報警項實現個性化監控報警。

日誌通過雲 Filebeat 元件寫入到 Elasticsearch 叢集,同時開放 Kibana 供使用者查詢。

圖片

整體的監控報警及日誌收集架構如下:

圖片

6. 健康檢查機制

隨著作業數的高速增長,出現了很多資源使用不合理的情況,比如前面提到的資源浪費的情況。使用者大多時候都是在對接新需求,支援新業務,很少回過頭來評估作業的資源配置是否合理,優化資源使用。所以平臺規劃了一版成本評估的模型,也就是現在說的健康檢查機制,平臺每天會針對作業做多維度的健康評分,使用者可以隨時在平臺上檢視單個作業的得分情況及最近 30 天的得分變化曲線。

低分作業會在使用者登入平臺時進行提示,並且定期發郵件提醒使用者進行優化、整改,在優化作業後使用者可以主動觸發重新評分,檢視優化效果。

圖片

我們引入了多維度,基於權重的評分策略,針對 CPU、記憶體使用率、是否存在空閒 Slot、GC 情況、Kafka 消費延遲、單核每秒處理資料量等多個維度的指標結合計算拓補圖進行分析評估,最終產生一個綜合分。

每個低分項都會顯示低分的原因及參考範圍,並顯示一些指導建議,輔助使用者進行優化。

我們新增了一個 Metric,用一個 0%~100% 的數字體現 TaskManagner CPU 利用率。這樣使用者可以直觀的評估 CPU 是否存在浪費的情況。

圖片

下面是作業評分的大致流程:首先我們會收集和整理執行作業的基本資訊和 Metrics 資訊。然後應用我們設定好的規則,得到基本評分和基礎建議資訊。最後將得分資訊和建議整合,綜合評判,得出綜合得分和最終的報告。使用者可以通過平臺檢視報告。對於得分較低的作業,我們會傳送報警給作業的歸屬使用者。

圖片

7. 自助診斷

如之前提到的痛點,使用者定位線上問題時,只能求助於我們平臺方,造成我們 on-call 工作量很大,同時使用者體驗也不好,鑑於此,所以我們上線了以下功能:

  1. 動態修改日誌級別:我們借鑑了 Storm 的修改日誌級別的方式,在 Flink 上實現了類似功能,通過擴充套件 REST API 和 RPC 介面的方法,支援修改指定 Logger 的到某一日誌級別,並支援設定一個過期時間,當過期後,改 Logger 的日誌會重新恢復為 INFO 級別;
  2. 支援自助檢視執行緒棧和堆記憶體資訊:Flink UI 中已經支援線上檢視執行緒棧 (jstack),我們直接複用了這個介面;還額外增加了檢視堆記憶體 (jmap) 的介面,方便使用者線上檢視;
  3. 支援線上生成、檢視火焰圖:火焰圖是定位程式效能問題的一大利器,我們利用了阿里的 arthas 元件,為 Flink 增加了線上檢視火焰圖的能力,使用者遇到效能問題時,可以快速評估效能瓶頸。

圖片

8. 基於 Checkpoint 複製的快速容災

圖片

當實時計算應用在重要業務場景時,單個 Yarn 叢集一旦出現故障且短期內不可恢復,那麼可能會對業務造成較大影響。

在此背景下,我們建設了 Yarn 多叢集架構,兩個獨立的 Yarn 各自對應一套獨立的 HDFS 環境,checkpoint 資料定期在兩個 HDFS 間相互複製。目前 checkpoint 複製的延遲穩定在 20 分鐘內。

同時,在平臺層面,我們把切換叢集的功能直接開放給使用者,使用者可以線上檢視 checkpoint 的複製情況,選擇合適的 checkpoint 後 (當然也可以選擇不從 checkpoint 恢復) 進行叢集切換,然後重啟作業,實現作業在叢集間的相對平滑的遷移。

三、基於 Flink 的實時生態建設

AutoStream 平臺的核心場景是支援實時計算開發人員的使用,使實時計算開發變得簡單高效、可監控、易運維。同時隨著平臺的逐步完善,我們開始摸索如何對 AutoStream 平臺進行重用,如何讓 Flink 應用在更多場景下。重用 AutoStream 有以下幾點優勢:

  1. Flink 本身是優秀的分散式計算框架,有著較高的計算效能,良好的容錯能力和成熟的狀態管理機制,社群蓬勃發展,功能及穩定性有保障;
  2. AutoStream 有著完善的監控和報警機制,作業執行在平臺上,無需單獨對接監控系統,同時 Flink 對 Metric 支援很友好,可以方便的新增新的 Metric;
  3. 大量的技術沉澱和運營經驗,通過兩年多的平臺建設,我們在 AutoStream 上已經實現了較為完善的 Flink 作業全生命週期的管理,並建設了 Jar Service 等基礎元件,通過簡單的上層介面包裝,就可以對接其他系統,讓其他系統具備實時計算的能力;
  4. 支援 Yarn 和 Kubernetes 部署。

圖片

基於以上幾點,我們在建設其他系統時,優先重用 AutoStream 平臺,以介面呼叫的方式進行對接,將 Flink 作業全流程的生命週期,完全託管給 AutoStream 平臺,各系統優先考慮實現自身的業務邏輯即可。

我們團隊內的 AutoDTS (接入及分發任務) 和 AutoKafka (Kafka 叢集複製) 系統目前就是依託於 AutoStream 建設的。簡單介紹一下整合的方式,以 AutoDTS 為例:

  1. 把任務 Flink 化,AutoDTS 上的接入、分發任務,都是以 Flink 作業的形式存在;
  2. 和 AutoStream 平臺對接,呼叫介面實現 Flink 作業的建立、修改、啟動、停止等操作。這裡 Flink 作業既可以是 Jar,也可以是 SQL 作業;
  3. AutoDTS 平臺根據業務場景,建設個性化的前端頁面,個性化的表單資料,表單提交後,可以將表單資料儲存到 MySQL 中;同時需要把作業資訊以及 Jar 包地址等資訊組裝成 AutoStream 介面定義的格式,通過介面呼叫在 AutoStream 平臺自動生成一個 Flink 任務,同時儲存這個 Flink 任務的 ID;
  4. 啟動 AutoDTS 的一個接入任務,直接呼叫 AutoStream 介面就實現了作業的啟動。

1. AutoDTS 資料接入分發平臺

AutoDTS 系統主要包含兩部分功能:

  1. 資料接入:將資料庫中的變更資料 (Change log) 實時寫入到 Kafka;
  2. 資料分發:將接入到 Kafka 的資料,實時寫入到其他儲存引擎。

1.1 AutoDTS 資料接入

下面是資料接入的架構圖:

圖片

我們維護了基於 Flink 的資料接入 SDK 並定義了統一的 JSON 資料格式,也就是說 MySQL Binlog,SQL Server、 TiDB 的變更資料接入到 Kafka 後,資料格式是一致的,下游業務使用時,基於統一格式做開發,無需關注原始業務庫的型別。

資料接入到 Kafka Topic 的同時,Topic 會自動註冊為一張 AutoStream 平臺上的流表,方便使用者使用。

資料接入基於 Flink 建設還有一個額外的好處,就是可以基於 Flink 的精確一次語義,低成本的實現精確一次資料接入,這對支援資料準確性要求很高的業務來說,是一個必要條件。

目前我們在做把業務表中的全量資料接入 Kafka Topic 中,基於 Kafka 的 compact 模式,可以實現 Topic 中同時包含存量資料和增量資料。這對於資料分發場景來說是十分友好的,目前如果想把資料實時同步到其他儲存引擎中,需要先基於排程系統,接入一次全量資料,然後再開啟實時分發任務,進行變更資料的實時分發。有了 Compact Topic 後,可以省去全量接入的操作。Flink1.12 版本已經對 Compact Topic 做支援,引入 upsert-kafka Connector [1]

[1] https://cwiki.apache.org/conf...

下面是一條樣例資料:

圖片

預設註冊到平臺上的流表是 Schemaless 的,使用者可以用 JSON 相關的 UDF 獲取其中的欄位資料。

圖片

下面是使用流表的示例:

圖片

1.2 AutoDTS 資料分發

圖片

我們已經知道,接入到 Kafka 中的資料是可以當做一張流表來使用的,而資料分發任務本質上是把這個流表的資料寫入到其他儲存引擎,鑑於 AutoStream 平臺已經支援多種 Table Sink (Connector),我們只需要根據使用者填寫的下游儲存的型別和地址等資訊,就可以通過拼裝 SQL 來實現資料的分發。

通過直接重用 Connector 的方式,最大化的避免了重複開發的工作。

下面是一個分發任務對應的 SQL 示例:

圖片

2. Kaka 多叢集架構

Kafka 在實際應用中,有些場景是需要做 Kafka 多叢集架構支援的,下面列舉幾個常見的場景:

  • 資料冗餘災備,實時複製資料到另一個備用叢集,當一個 Kafka 叢集不可用時,可以讓應用切換到備用叢集,快速恢復業務;
  • 叢集遷移,當機房合同到期,或者上雲時,都需要做叢集的遷移,此時需要把叢集資料整體複製到新機房的叢集,讓業務相對平滑遷移;
  • 讀寫分離場景,使用 Kafka 時,大多數情況都是讀多寫少,為保證資料寫入的穩定性,可以選擇建設 Kafka 讀寫分離叢集。

我們目前建設了 Kafka 多叢集架構,和 Flink 相關的主要有兩塊內容:

  1. Kafka 叢集間資料複製的程式執行在 Flink 叢集中;
  2. 改造了 Flink Kafka Connector,支援快速切換 Kafka 叢集。

2.1 整體架構

圖片

先來看一下 Kafka 叢集間的資料複製,這是建設多叢集架構的基礎。我們是使用 MirrorMaker2 來實現資料複製的,我們把 MirrorMaker2 改造成普通的 Flink 作業,執行在 Flink 叢集中。

我們引入了 Route Service 和 Kafka SDK,實現客戶端快速切換訪問的 Kafka 叢集。

客戶端需要依賴我們自己釋出的 Kafka SDK,並且配置中不再指定 bootstrap.servers 引數,而是通過設定 cluster.code 引數來宣告自己要訪問的叢集。 SDK 會根據 cluster.code 引數,訪問 Route Service 獲取叢集真正的地址,然後建立 Producer/Consumer 開始生產/消費資料。

SDK 會監聽路由規則的變化,當需要切換叢集時,只需要在 Route Service 後臺切換路由規則,SDK 發現路由叢集發生變化時,會重啟 Producer/Consumer 例項,切換到新叢集。

如果是消費者發生了叢集切換,由於 Cluster1 和 Cluster2 中 Topic 的 offset 是不同的,需要通過 Offset Mapping Service 來獲取當前 Consumer Group 在 Cluster2 中的 offset,然後從這些 Offset 開始消費,實現相對平滑的叢集切換。

2.2 Kafka 叢集間的資料複製

我們使用 MirrorMaker2 來實現叢集間的資料複製,MirrorMaker2 是 Kafka 2.4 版本引入的,具體以下特性:

  • 自動識別新的 Topic 和 Partition;
  • 自動同步 Topic 配置:Topic 的配置會自動同步到目標叢集;
  • 自動同步 ACL;
  • 提供 Offset 的轉換工具:支援根據源叢集、目標叢集及 Group 資訊,獲取到該 Group 在目標叢集的中對應的 Offset 資訊;
  • 支援擴充套件黑白名單策略:可以靈活定製,動態生效。

clusters = primary, backup

primary.bootstrap.servers = vip1:9091

backup.bootstrap.servers = vip2:9092

primary->backup.enabled = true

backup->primary.enabled = true

這段配置完成 primary 到 backup 叢集的雙向資料複製,primary 叢集中的 topic1 中的資料會複製到 backup 叢集中的 primary.topic1 這個 Topic 中,目標叢集的Topic 命名規則是 sourceCluster.sourceTopicName,可以通過實現 ReplicationPolicy 介面來自定義命名策略。

圖片

2.3 MirrorMaker2 相關的 Topic 介紹

  • 源叢集中的 Topic

    heartbeats:儲存心跳資料;

    mm2-offset-syncs.targetCluster.internal:儲存源叢集 (upstreamOffset) 和目標叢集的 offset(downstreamOffset) 對應關係。

  • 目標叢集中的 Topic

    mm2-configs.sourceCluster.internal:connect 框架自帶,用來儲存配置;

    mm2-offsets.sourceCluster.internal:connect 框架自帶,用來儲存 WorkerSourceTask 當前處理的 offset,mm2 場景下是為了當前資料同步到源叢集 topic partition 的哪一個 offset,這個更像是 Flink 的 checkpoint 概念;

    mm2-status.sourceCluster.internal:connect 框架自帶,用來儲存 connector 狀態。

上面三個用的都是 connect runtime 模組中的 KafkaBasedLog 工具類,這個工具類可以讀寫一個 compact 模式的 topic 資料,此時 MirrorMaker2 把 topic 當作 KV 儲存使用。

sourceCluster.checkpoints.internal:記錄 sourceCluster consumer group 在當前叢集對應的 offset,mm2 會定期從源 kafka 叢集讀取 topic 對應的 consumer group 提交的 offset, 並寫到目標叢集的 sourceCluster.checkpoints.internal topic 中。

圖片

2.4 MirrorMaker2 的部署

下面是 MirrorMaker2 作業執行的流程,在 AutoKafka 平臺上建立一個資料複製作業,會呼叫 AutoStream 平臺介面,相應的建立一個 MM2 型別的作業。啟動作業時,會呼叫 AutoStream 平臺的介面把 MM2 作業提交到 Flink 叢集中執行。

圖片

2.5 路由服務

Route Service 負責處理客戶端的路由請求,根據客戶端的資訊匹配合適的路由規則,將最終路由結果,也就是叢集資訊返回給客戶端。

支援基於叢集名稱、Topic、Group、ClientID 以及客戶端自定義的引數靈活配置路由規則。

下面的例子就是將 Flink 作業 ID 為 1234 的消費者,路由到 cluster_a1 叢集。

圖片

2.6 Kafka SDK

使用原生的 kafka-clients 是無法和 Route Service 進行通訊的,客戶端需要依賴我們提供的 Kafka SDK (汽車之家內部開發的 SDK) 能和 Route Service 通訊,實現動態路由的效果。

Kafka SDK 實現了 Producer、Consumer 介面,本質是 kafka-clients 的代理,業務做較少的改動就可以引入 Kafka SDK。

業務依賴 Kafka SDK 後,Kafka SDK 會負責和 Route Service 通訊,監聽路由變化,當發現路由的叢集發生變化時,會 close 當前的 Producer/Consumer,建立新的 Producer/Consumer,訪問新的叢集。

此外 Kafka SDK 還負責將 Producer、Consumer 的 metric 統一上報到雲監控系統的 prometheus,通過檢視平臺預先配置好的儀表盤,可以清晰的看到業務的生產、消費情況。

同時 SDK 會收集一些資訊,比如應用名稱、IP 埠、程式號等,這些資訊可以在 AutoKafka 平臺上查到,方便我們和使用者共同定位問題。

圖片

2.7 Offset Mapping Service

當 Consumer 的路由發生變化並切換叢集時,情況有一些複雜,因為目前 MirrorMaker2 是先把資料從源叢集消費出來,再寫入到目標叢集的,同一條資料可以確保寫入到目標 topic 的相同分割槽,但是 offset 和源叢集是不同的。

針對這種 offset 不一致的情況,MirrorMaker2 會消費源叢集的 __consumer_offsets 資料,加上目標叢集對應的 offset,寫入到目標叢集的 sourceCluster.checkpoints.internal topic 中。

同時,源叢集的 mm2-offset-syncs.targetCluster.internal topic 記錄了源叢集和目標叢集 offset 的對映關係,結合這兩個 topic,我們建設了 Offset Mapping Service 來完成目標叢集的 offset 的轉換工作。

所以當 Consumer 需要切換叢集時,會呼叫 Offset Mapping Service 的介面,獲取到目標叢集的 offsets,然後主動 seek 到這些位置開始消費,這樣實現相對平滑的叢集切換工作。

圖片

2.8 Flink 與 Kafka 多叢集架構的整合

由於 Kafka SDK 相容 kafka-clients 的用法,使用者只需要更換依賴,然後設定 cluster.code、Flink.id 等引數即可。

當 Producer/Consumer 發生叢集切換後,由於建立了新的 Producer/Consumer 例項,Kafka 的 metric 資料沒有重新註冊,導致 metric 資料無法正常上報。我們在 AbstractMetricGroup 類中增加了 unregister 方法,在監聽 Producer/Consumer 的切換事件時,重新註冊 kafka metrics 就可以了。

至此我們完成了 Flink 對 Kafka 多叢集架構的支援。

圖片

四、後續規劃

圖片

  1. 目前我們支援的資料統計類場景大多是基於流量資料或使用者行為資料的,這些場景對精確一次的語義要求不高,隨著目前社群對 Change Log 支援的逐步完善,同時我們的資料接入體系是支援精確一次語義的,並且正在做業務表全量接入到 Kafka 的功能,所以後續可以實現精確一次的資料統計,支援交易、線索、金融類的統計需求。
  2. 一些公司已經提出湖倉一體的理念,資料湖技術確實可以解決一些原有數倉架構的痛點,比如資料不支援更新操作,無法做到準實時的資料查詢。目前我們在做一些 Flink 和 Iceberg、Hudi 整合的一些嘗試,後續會在公司尋找場景並落地。

更多 Flink 相關技術問題,可掃碼加入社群釘釘交流群;

第一時間獲取最新技術文章和社群動態,請關注公眾號~

img

相關文章