從5分鐘到60秒,袋鼠雲數棧在熱重啟技術上的提效探索之路

數棧DTinsight發表於2023-03-09

更好地提高效率一直以來是 的主要目標之一。當前數棧客戶的實時任務都是基於 Per-Job 模式執行的,客戶在進行一些任務引數的修改之後,只能先取消當前任務,再選擇 CheckPoint 恢復或者重新執行,整個過程需要3-5分鐘,比較浪費時間。為了達到提高效率的目的,我們針對 Per-Job 任務的整體流程分析,進行了相關探索。

下文和大家聊聊數棧在 方面的探索之路。

熱重啟是什麼?

旨在複用當前 Per-Job 叢集的相關資源,減少重新建立叢集以及申請資源的耗時,同時透過 CheckPoint 機制 。

Flink 的 Per-Job 模式是指每個任務都會對應一個獨立的 。在任務提交的時候,會建立一個 Flink 叢集進行任務的執行,整個叢集只為這一個任務進行服務。同時 Flink 叢集不允許繼續提交任務,導致任務修改之後,只能 Cancel 當前任務。重新提交修改後的任務,建立一個新的 Flink 叢集進行執行。

經過分析,耗時主要是由於以下兩部分原因造成:

• Client 需要在 Yarn 上啟動一個 Flink 叢集,這一部分是客戶端耗時最多的部分,因為這一部分包括上傳 jar,上傳檔案到Hdfs 上,申請資源啟動 Flink 叢集,都是比較耗時的步驟

• 叢集執行的時候需要申請資源等操作也十分耗時

我們思考如果僅僅是一些任務引數或者 Sql 邏輯的修改,而不涉及程式碼上的修改,那麼 PerJob 任務是否可以類似 Session 模式進行改造,支援 JobGraph 的重新提交,解決 Client 需要啟動一個 Flink 叢集的耗時問題,大大提高提交效率。

同時 ,如果並行度改變,只需要申請新增加的資源,已有的資源不需要再重複向 Yarn 的 Resourcemanager 申請。

熱重啟改造後的流程

Flink 中 Per-Job 任務執行的整體流程大概如下所示:

客戶端流程

• Client 端建立 JobGraph

• 上傳 JobGraph 到 hdfs 裡

• 透過 YarnClient 提交一個 YarnApplication,執行一個 Flink 任務

• 獲取結果

Flink 叢集流程

• 啟動 Flink 叢集,啟動 WebMonitor,ResourceManager,Dispatcher 元件

• Client 端上傳到遠端檔案服務裡的 JobGraph 會被反序列出來由 DIspatcher 持有;

• DIspatcher 會根據此 JobGraph 建立 JobManagerRunner 物件進行執行;

• JobManagerRunner 會交由內部的 ScheduleNg 進行排程執行任務:

a.構建 ScheduleNg 時,會將 JobGraph 轉為ExecutionGraph

b. ScheduleNg 根據 ExecutionGraph 進行排程,執行任務

• 任務執行,等待任務執行結束,進行相應的回撥處理

file

從上圖我們可以看出,一個 Per-Job 任務的執行主要包括兩部分:一部分是客戶端上傳檔案 jar 等操作後,直接上傳任務到 Yarn 上進行 Flink 任務的啟動,第二部分是Flink叢集的啟動,然後對客戶端上傳到遠端檔案的 JobGraph 進行處理。因此為了最佳化 Per-Job 下的效率,我們對這兩部分進行了改造。

想法邏輯是,叢集首先改造支援 JobGraph 的重新提交,然後 DIspatcher 處理 JobGraph 的時候,不會建立新的 JobMaster ,而是將當前現有的 JobGraph 裡的一些資訊填充到新的 JobGraph 裡,比如當前任務的 CheckPoint 資訊等。任務最終的排程執行是 JobMaster 裡的 ScheduleNg 物件。因此我們認為只需要 ,其餘的元件都可以複用。

下圖即為我們熱重啟技術改造後的一個大致流程:

file

熱重啟技術改造後流程

• WebMonitor 支援任務的提交

• DIspatcher 將新的 JobGraph 快取

• 取消當前任務,等待非同步回撥

• 返回結果給客戶端

• 在任務取消的非同步回撥裡主要是熱重啟的重點改造部分:

a.判斷當前是否有新的 JobGraph 快取,有的話進入熱重啟邏輯,無則走當前現有邏輯

b.獲取取消任務的 CheckPoint 資訊,填充到新的 JobGraph 裡

c.將 jobGrap 更新到 JobMaster 裡,清理以前 JobGraph 的快取資訊

d.把 JobMaster 裡 SlotPool 管理的資源釋放掉

e.JobMaster 重新建立 ScheduleNg 並排程執行,至此新的 JobGraph 就被成功排程執行了

熱重啟改造部分詳解

JobGraph 介紹

在上述流程中, 是整體流轉的主要物件,後續的一切操作都是圍繞著 JobGraph 進行處理,所以這裡先對 JobGraph 進行介紹。

JobGraph 是 Flink 作業的內部表示,是一個 ,主要是將一些可以最佳化的運算元節點合併為一個節點。從下圖可知,一個完整的 JobGraph 圖包含了 Source Sink Transform 節點,以及節點的輸出 和輸入邊 JobEdge 。在除了 Application 模式外,其餘的提交模式下,JobGraph 是在 Client 建立的,然後透過 Rest 請求提交給 Flink 叢集進行處理。

file

看完 JobGraph 此類結構,可以得出以下這些資訊:

· taskVertices:上圖中的每個頂點對應一個 ,taskVertices 維護了 jobGraph 圖裡的各個 jobVertex

· snapshotSettings:checkponit 相關的配置資訊,如 CheckPoint 的間隔時間等

· savepointRestoreSettings:任務恢復的 checkpoint 檔案資訊,熱重啟中,新的 jobGraph 會將上一個任務的 checkPoint 位點資訊填充到這個引數裡,新的任務會在 位點處進行恢復執行

· jobConfiguration:整個 job 的相關配置資訊

· userJars & calsspath:任務執行過程中需要的一些 jar 以及 classpath 相關資訊

其中 JobVertex 是 jobGraph 裡非常重要的物件,再看下此類結構:JobVertex 主要儲存了JobEdge以及 IntermediateDataSet 和並行度等相關資訊。對於一個 JobVertex 來說,IntermediateDataSet 是作為 JobVertex 的輸出,而 JobEdge 是其輸入。

file

WebMonitor 改造

WebMonitor 元件是 Flink 的 ,可以透過 Rest Api 進行 Flink 叢集的狀態、任務、指標等資訊的查詢,同時支援任務的提交、取消、觸發 SavePoint 等操作。

Per-Job 模式下 Flink 叢集是不支援客戶端繼續提交任務執行的,因此需要對 WebMonitor 進行改造,類似 Session 下支援同一個 Flink 叢集能繼續提交 JobGraph 並執行。

從下圖可以看出 WebMonitor 元件啟動時,其本質是 為核心的一個 Web 端點。啟動時的主要流程如下:

• 建立 Router,管理 http 請求和處理器 handler 的對映關係

• initializeHandlers 初始化所有的 handler,不同的叢集對應的 WebMonitor 提供的 不同,所以 handlers 也是不同的

• 將 handlers 註冊到 router,完成 URL 以及請求方式(GET,POST,DELETE,PUT)和 Handler 的對映關係

• 建立一個 Netty 的 handler,包裝下 router,然後註冊到 Netty 的 pipeline 裡

file

WebMonitor 支援的各種 Rest 請求其實最終是交給一個個的 handler 進行處理,透過 Router 對這些 handler 進行維護,其內部維護了一個 url 以及 Rest 請求方式與 handler 的對映關係。接收 Client 端的 Rest 請求之後,Router 找到對應的處理器 handler,交由 handler 進行最終的處理並返回結果。

因為 Per-job 叢集是不支援 Client 端繼續提交任務的,所以其 initializeHandlers 方法初始化出的 handlers 不包含處理任務提交的 handler,導致 router 找不到對應的 handler 報錯,因此需要在 initializeHandlers 裡將處理任務提交的 handler 註冊進去 。

file

JobSubmitHandler 處理請求的主要邏輯如下圖所示。核心是從 Rest 請求的 Body 裡反序列化得到 JobGraph,反序列化獲取的 Jobgraph 透過 傳送給 Dispatcher 進行後續提交處理。

file

這樣 Client 端只需要重新生成 JobGraph 然後提交即可,避免了重新上傳 jar 到 hdfs,以及避免浪費重新向 yarn 叢集申請資源啟動 AppMaster 的時間。

Dispatcher 改造

DisPatcher 顧名思義是一個 ,其主要功能是 Flink 叢集接收到關於 Job 的提交、取消、觸發 SavePoint 等操作,分發到對應的各個 JobMaster 進行處理,或者建立新的 JobMaster 進行任務的執行。

DisPatcher 處理任務提交的核心流程是根據 JobGraph 建立一個 JobManagerRunner 物件並啟動,然後將其包裝成一個 DispatcherJob 快取在內部。任務的具體排程執行交由建立的 JobManagerRunner 進行非同步處理。

JobManagerRunner 其內部的具體操作其實是 JobMasterService,主要實現類就是 JobMaster。JobMaster 內部有兩個主要物件分別是:

· ScheduleNg: 負責 JobGraph 轉為 ExecutionGraph,然後對 Job 進行排程執行

· SlotPool:負責 Slot 資源的申請以及管理

以上便是 Dispatcher 處理的主要流程。當前改造之後只是支援了任務的重新提交執行,但是新的任務仍然是對應一個新的 JobMaster,其實就是一個類似 Session 的處理,所以為了達到熱重啟的效果,需要進行以下的改造。

主流程的改造邏輯如下:

• 增加了一個 hotRestartJobGraph 欄位,將新的 JobGraph 物件賦予此欄位

• Dispatcher 將快取的正在執行的任務 cancel,對非同步返回結果進行回撥處理

• 直接返回 Client 結果

因為 Flink 整體是 的,原始碼裡充滿了大量的 CompletableFuture 回撥的處理,主流程僅僅對提交的 JobGraph 進行了一個快取處理,熱重啟的主要步驟在任務取消的回撥裡進行處理:

• 判斷 hotRestartJobGraph 是否為空,如果不為空則進行熱重啟處理,為空則用以前的邏輯,整個 Per-job 叢集關閉

• 獲取取消任務的最後一個 CheckPoint 位點

• 將 CheckPoint 位點資訊填充到新的 Jobgraph 裡

• 反射將上一個 Jobgraph 生成的 JobManagerRunner 和 jobMaster 兩個物件的JobGraph 欄位用新的 JobGraph 替換掉

• jobMaster 物件根據 jobGraph 重新生成 scheduleNg 進行排程執行

• jobMaster 的 slotPool 在 內,會快取已經釋放掉的 slot,需要把這部分快取清空

• MiniDispatcher 的 close 方法修改下,如果 hotRestartJobGraph 不為空則不進行叢集的關閉

• hotRestartJobGraph 置空

注意上述只是主要的一些改造地方,其餘一些邊緣的細節處理就不再進行贅述。

所以在熱重啟中,DIspatcher 是不會對每一個 JobGraph 建立新的 JobMaster 物件。透過將新的 JobGraph 更新到 JobMaster 裡,內部僅僅 ScheduleNg 進行了重新構建,其餘的元件都進行了複用,比如 SlotPool。

ScheduleNg 之所以需要重新構建是因為 JobGraph 轉為 ExecutionGraph 是需要 ScheduleNg 在構建的時候建立的,因此需要重新構建一個 ScheduleNg 進行任務的排程執行,這樣達到了 ,大大提升了效率。

Slot 資源的複用

Flink 中對於資源的抽象主要是 Slot,其各個元件對 Slot 的管理是由不同的元件處理的:

· Flink 的 ResourceManager 裡是 SlotManager 管理,主要是任務的資源申請以及管理

· JobMaster 裡管理 Slot 是 SlotPool ,主要是對當前任務申請的 slot 進行管理

· TaskExecutor 裡則是S lotTable 對 Slot 進行管理,維護 JobId 和 Slot 的關係

在熱重啟中,上一個任務取消之後,JobMaster 裡 SlotPool 管理的 Slot 狀態由已分配改為可用。這樣在 JobMaster 透過新的 ScheduleNg 進行重新排程,會複用 SlotPool 裡快取的 Slot,這個時候其實是有問題的。在 TaskExecutor 接收到任務的時候會報錯,在其 裡找不到新任務的 JobId,因為此時 TaskExecutor 維護的 Jobid 還是上一個任務的。

所以 JobMaster 的 SlotPool 需要釋放掉其 ,注意只是清理內部快取,此時 TaskManager 的 Slot 槽資源還沒被釋放,仍然被 Resourcemanager 的 SlotManager 管理著。這樣 SlotPool 發現內部沒可用的 Slot 槽就會和 ResourceManager 的 SlotManager 申請資源,SlotManager 就仍然複用了以前的 Slot 槽並且將新的 JobGraph 的 jobId 透過 rpc 請求註冊進了 TaskExecutor。從而達到了 slot 槽資源的複用,減少了 Flink 叢集的 ResourceManager 重新向 Yarn 的 ResourceManager 申請資源。

總結

數棧在 Per-job 模式下,為了儘快看到任務修改後的效果,在業務允許情況下,透過 ,減少了大量時間,極大地提高了效率。在開發驗證中,以前一個任務等待任務結束以及重新提交執行總流程超過4分鐘,但是在熱重啟情況下控制在1分鐘以內就已經可以進行排程執行。

未來我們將會把熱重啟的場景進一步豐富,支援更多場景下的熱重啟技術,如 jar 的程式碼修改,如何更新環境裡的 jar,支援 k8s 場景等。

一直以來高度重視產品升級和使用者體驗,用誠心傾聽使用者需求,新的一年我們將繼續保持產品升級節奏,以提效為目標滿足不同行業使用者的更多需求。為了更好的產品,更佳的使用者體驗,數棧一直在路上。

《資料治理行業實踐白皮書》下載地址:


想了解更多有關袋鼠雲大資料產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szitpub


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

相關文章