日均處理萬億資料!Flink在快手的應用實踐與技術演進之路

ApacheFlink發表於2019-09-06

作者:董亭亭
整理:蔣曉峰

作者介紹:董亭亭,快手大資料架構實時計算引擎團隊負責人。目前負責 Flink 引擎在快手內的研發、應用以及周邊子系統建設。2013 年畢業於大連理工大學,曾就職於奇虎 360、58 集團。主要研究領域包括:分散式計算、排程系統、分散式儲存等系統。

本次的分享包括以下三個部分:

  1. 介紹 Flink 在快手的應用場景以及目前規模;
  2. 介紹 Flink 在落地過程的技術演進過程;
  3. 討論 Flink 在快手的未來計劃。

一.Flink 在快手應用場景與規模

1. Flink 在快手應用場景

_05_jpeg

快手計算鏈路是從 DB/Binlog 以及 WebService Log 實時入到 Kafka 中,然後接入 Flink 做實時計算,其中包括實時 ETL、實時分析、Interval Join 以及實時訓練,最後的結果存到 Druid、ES 或者 HBase 裡面,後面接入一些資料應用產品;同時這一份 Kafka 資料實時 Dump 一份到 Hadoop 叢集,然後接入離線計算。

_06_jpeg

Flink 在快手應用的類別主要分為三大類:

  • 80% 統計監控:實時統計,包括各項資料的指標,監控項報警,用於輔助業務進行實時分析和監控;
  • 15% 資料處理:對資料的清洗、拆分、Join 等邏輯處理,例如大 Topic 的資料拆分、清洗;
  • 5% 資料處理:實時業務處理,針對特定業務邏輯的實時處理,例如實時排程。

_07_jpeg

Flink 在快手應用的典型場景包括:

  • 快手是分享短視訊跟直播的平臺,快手短視訊、直播的質量監控是通過 Flink 進行實時統計,比如直播觀眾端、主播端的播放量、卡頓率、開播失敗率等跟直播質量相關的多種監控指標;
  • 使用者增長分析,實時統計各投放渠道拉新情況,根據效果實時調整各渠道的投放量;
  • 實時資料處理,廣告展現流、點選流實時 Join,客戶端日誌的拆分等;
  • 直播 CDN 排程,實時監控各 CDN 廠商質量,通過 Flink 實時訓練調整各個CDN廠商流量配比。

2.Flink 叢集規模

_08_jpeg

快手目前叢集規模有 1500 臺左右,作業數量大約是 500 左右,日處理條目數總共有 1.7 萬億,峰值處理條目數大約是 3.7 千萬。叢集部署都是 On Yarn 模式,分為離線叢集和實時叢集兩類叢集,其中離線叢集混合部署,機器通過標籤進行物理隔離,實時叢集是 Flink 專用叢集,針對隔離性、穩定性要求極高的業務部署。

二.快手 Flink 技術演進

快手 Flink 技術演進主要分為三部分:

  1. 基於特定場景優化,包括 Interval Join 場景優化;
  2. 穩定性改進,包括資料來源控速,JobManager 穩定性,作業頻繁失敗;
  3. 平臺建設。

1.場景優化

1.1 Interval Join 應用場景

_11_jpeg

Interval Join 在快手的一個應用場景是廣告展現點選流實時 Join 場景:開啟快手 App 可能會收到廣告服務推薦的廣告視訊,使用者有時會點選展現的廣告視訊。這樣在後端形成兩份資料流,一份是廣告展現日誌,一份是客戶端點選日誌。這兩份資料需進行實時 Join,將 Join 結果作為樣本資料用於模型訓練,訓練出的模型會被推送到線上的廣告服務。該場景下展現以後 20 分鐘的點選被認為是有效點選,實時 Join 邏輯則是點選資料 Join 過去 20 分鐘展現。其中,展現流的資料量相對比較大,20 分鐘資料在 1 TB 以上。最初實時 Join 過程是業務自己實現,通過 Redis 快取廣告展現日誌,Kafka 延遲消費客戶端點選日誌實現 Join 邏輯,該方式缺點是實時性不高,並且隨著業務增長需要堆積更多機器,運維成本非常高。基於 Flink 使用 Interval Join 完美契合此場景,並且實時性高,能夠實時輸出 Join 後的結果資料,對業務來說維護成本非常低,只需要維護一個 Flink 作業即可。

1.2 Interval Join 場景優化

_12_jpeg

1.2.1 Interval Join 原理:

Flink 實現 Interval join 的原理:兩條流資料快取在內部 State 中,任意一資料到達,獲取對面流相應時間範圍資料,執行 joinFunction 進行 Join。隨著時間的推進,State 中兩條流相應時間範圍的資料會被清理。

在前面提到的廣告應用場景 Join 過去 20 分鐘資料,假設兩個流的資料完全有序到達,Stream A 作為展現流快取過去 20 分鐘資料,Stream B 作為點選流每來一條資料到對面 Join 過去 20 分鐘資料即可。

Flink 實現 Interval Join:

KeyedStreamA.intervalJoin(KeyedStreamB)
         .between(Time.minutes(0),Time.minutes(20))
         .process(joinFunction)
1.2.2 狀態儲存策略選擇

_13_jpeg

關於狀態儲存策略選擇,生產環境狀態儲存 Backend 有兩種方式:

  1. FsStateBackend:State 儲存在記憶體,Checkpoint 時持久化到 HDFS;
  2. RocksDBStateBackend:State 儲存在 RocksDB 例項,可增量 Checkpoint,適合超大 State。在廣告場景下展現流 20 分鐘資料有 1 TB 以上,從節省記憶體等方面綜合考慮,快手最終選擇的是 RocksDBStateBackend。

在 Interval join 場景下,RocksDB 狀態儲存方式是將兩個流的資料存在兩個 Column Family 裡,RowKey 根據 keyGroupId+joinKey+ts 方式組織。

1.2.3 RocksDB 訪問效能問題

_14_

Flink 作業上線遇到的第一個問題是 RocksDB 訪問效能問題,表現為:

  • 作業在執行一段時間之後出現反壓,吞吐下降。
  • 通過 Jstack 發現程式邏輯頻繁處於 RocksDB get 請求處。
  • 通過 Top 發現存在單執行緒 CPU 持續被打滿。

進一步對問題分析,發現:該場景下,Flink 內部基於 RocksDB State 狀態儲存時,獲取某個 Join key 值某段範圍的資料,是通過字首掃描的方式獲取某個 Join key 字首的 entries 集合,然後再判斷哪些資料在相應的時間範圍內。字首掃描的方式會導致掃描大量的無效資料,掃描的資料大多快取在 PageCache 中,在 Decode 資料判斷資料是否為 Delete 時,消耗大量 CPU。

以上圖場景為例,藍色部分為目標資料,紅色部分為上下邊界之外的資料,字首掃描時會過多掃描紅色部分無用資料,在對該大量無效資料做處理時,將單執行緒 CPU 消耗盡。

1.2.4 針對 RocksDB 訪問效能優化

_15

快手在 Interval join 該場景下對 RocksDB 的訪問方式做了以下優化:

  • 在 Interval join 場景下,是可以精確的確定需訪問的資料邊界範圍。所以用全 Key 範圍掃描代替字首掃描,精確拼出查詢上下邊界 Full Key 即 keyGroupId+joinKey+ts[lower,upper]。
  • 範圍查詢 RocksDB ,可以更加精確 Seek 到上下邊界,避免無效資料掃描和校驗。

優化後的效果:P99 查詢時延效能提升 10 倍,即 nextKey 獲取 RocksDB 一條資料, P99 時延由 1000 毫秒到 100 毫秒以內。 作業吞吐反壓問題進而得到解決。

1.2.5 RocksDB 磁碟壓力問題

_16_jpeg

Flink 作業上線遇到的第二個問題是隨著業務的增長, RocksDB 所在磁碟壓力即將達到上限,高峰時磁碟 util 達到 90%,寫吞吐在 150 MB/s。詳細分析發現,該問題是由以下幾個原因疊加導致:

  • Flink 機器選型為計算型,大記憶體、單塊 HDD 盤,在叢集規模不是很大的情況下,單個機器會有 4-5 個該作業 Container,同時使用一塊 HDD 盤。
  • RocksDB 後臺會頻繁進行 Compaction 有寫放大情況,同時 Checkpoint 也在寫磁碟。

針對 RocksDB 磁碟壓力,快手內部做了以下優化:

  • 針對 RocksDB 引數進行調優,目的是減少 Compaction IO 量。優化後 IO 總量有一半左右的下降。
  • 為更加方便的調整 RocksDB 引數,在 Flink 框架層新增 Large State RocksDB 配置套餐。同時支援 RocksDBStateBackend 自定義配置各種 RocksDB 引數。
  • 未來計劃,考慮將 State 用共享儲存的方式儲存,進一步做到減少 IO 總量,並且快速Checkpoint 和恢復。

2.穩定性改進

_17_jpeg

首先介紹下視訊質量監控排程應用背景,有多個 Kafka Topic 儲存短視訊、直播相關質量日誌,包括短視訊上傳/下載、直播觀眾端日誌,主播端上報日誌等。Flink Job 讀取相應 Topic 資料實時統計各類指標,包括播放量、卡頓率、黑屏率以及開播失敗率等。指標資料會存到 Druid 提供後續相應的報警監控以及多維度的指標分析。同時還有一條流是進行直播 CDN 排程,也是通過 Flink Job 實時訓練、調整各 CDN 廠商的流量配比。以上 Kafka Topic 資料會同時落一份到 Hadoop 叢集,用於離線補償資料。實時計算跟離線補資料的過程共用同一份 Flink 程式碼,針對不同的資料來源,分別讀取 Kafka 資料或 HDFS 資料。

2.1 資料來源控速

_18

視訊應用場景下遇到的問題是:作業 DAG 比較複雜,同時從多個 Topic 讀取資料。一旦作業異常,作業失敗從較早狀態恢復,需要讀取部分歷史資料。此時,不同 Source 併發讀取資料速度不可控,會導致 Window 類運算元 State 堆積、作業效能變差,最終導致作業恢復失敗。 另外,離線補資料,從不同 HDFS 檔案讀資料同樣會遇到讀取資料不可控問題。在此之前,實時場景下臨時解決辦法是重置 GroupID 丟棄歷史資料,使得從最新位置開始消費。

針對該問題我們希望從源頭控制多個 Source 併發讀取速度,所以設計了從 Source 源控速的策略。

Source 控速策略

_19_jpeg

Source 控速策略是 :

  • SourceTask 共享速度狀態<id,ctime,watermark,speed>提供給 JobManager。
  • JobManager 引入 SourceCoordinator,該 Coordinator 擁有全域性速度視角,制定相應的策略,並將限速策略下發給 SourceTask。
  • SourceTask 根據 JobManager 下發的速度調節資訊執行相應控速邏輯。
  • 一個小細節是 DAG 圖有子圖的話, 不同子圖 Source 源之間互相不影響。

Source 控速策略詳細細節

_21_jpeg

SourceTask 共享狀態

  • SourceTask 定期彙報狀態給 JobManager,預設 10 s 間隔。
  • 彙報內容為<id,clocktime,watermark,speed>。

協調中心 SourceCoordinator

  • 限速閾值:最快併發 Watermark - 最慢併發 Watermark > ∆t(預設 5 分鐘)。只要在達到限速閾值情況下,才進行限速策略制定。
  • 全域性預測:各併發 targetWatermark=base+speed*time;Coordinator 先進行全域性預測,預測各併發接下來時間間隔能執行到的 Watermark 位置。
  • 全域性決策:targetWatermark = 預測最慢 Watermark+∆t/2;Coordinator 根據全域性預測結果,取預測最慢併發的 Watermark 值再浮動一個範圍作為下個週期全侷限速決策的目標值。
  • 限速資訊下發:<targetTime,targetWatermark>。將全域性決策的資訊下發給所有的 Source task,限速資訊包括下一個目標的時間和目標的 Watermark 位置。

以上圖為例,A 時刻,4 個併發分別到達如圖所示位置,為 A+interval 的時刻做預測,圖中藍色虛線為預測各併發能夠到達的位置,選擇最慢的併發的 Watermark 位置,浮動範圍值為 Watermark + ∆t/2 的時間,圖中鮮紅色虛線部分為限速的目標 Watermark,以此作為全域性決策發給下游 Task。

_22_jpeg

SourceTask 限速控制

  • SourceTask 獲取到限速資訊<targetTime,targetWatermark>後,進行限速控制。
  • 以 KafkaSource 為例,KafkaFetcher 獲取資料時,根據限速資訊 Check 當前進度,確定是否需要限速等待。

該方案中,還有一些其他考慮,例如:

  • 時間屬性:只針對 EventTime 情況下進行限速執行。
  • 開關控制:支援作業開關控制是否開啟 Source 限速策略。
  • DAG 子圖 Source 源之間互相不影響。
  • 是否會影響 CheckPoint Barrier 下發。
  • 資料來源傳送速度不恆定,Watermark 突變情況。

Source 控速結果

_23_jpeg

拿線上作業,使用 Kafka 從最早位置(2 days ago)開始消費。如上圖,不限速情況下State 持續增大,最終作業掛掉。使用限速策略後,最開始 State 有緩慢上升,但是 State 大小可控,最終能平穩追上最新資料,並 State 持續在 40 G 左右。

2.2 JobManager 穩定性

_24_jpeg

關於 JobManager 穩定性,遇到了兩類 Case,表現均為:JobManager 在大併發作業場景 WebUI 卡頓明顯,作業排程會超時。進一步分析了兩種場景下的問題原因。

場景一,JobManager 記憶體壓力大問題。JobManager 需要控制刪除已完成的 Checkpoint 在 HDFS 上的路徑。在 NameNode 壓力大時,Completed CheckPoint 路徑刪除慢,導致CheckPoint Path 在記憶體中堆積。 原來刪除某一次 Checkpoint 路徑策略為:每刪除目錄下一個檔案,需 List 該目錄判斷是否為空,如為空將目錄刪除。在大的 Checkpoint 路徑下, List 目錄操作為代價較大的操作。針對該邏輯進行優化,刪除檔案時直接呼叫 HDFS delete(path,false) 操作,語義保持一致,並且開銷小。

場景二,該 Case 發生在 Yarn Cgroup 功能上線之後,JobManager G1 GC 過程變慢導致阻塞應用執行緒。AppMaster 申請 CPU 個數硬編碼為1,在上線 Cgroup 之後可用的 CPU 資源受到限制。解決該問題的方法為,支援 AppMaster 申請 CPU 個數引數化配置。

2.3 作業頻繁失敗

_25_jpeg

機器故障造成作業頻繁失敗,具體的場景也有兩種:

場景一:磁碟問題導致作業持續排程失敗。磁碟出問題導致一些 Buffer 檔案找不到。又因為 TaskManager 不感知磁碟健康狀況,會頻繁排程作業到該 TaskManager,作業頻繁失敗。

場景二:某臺機器有問題導致 TaskManager 在某臺機器上頻繁出 Core,陸續分配新的 TaskManager 到這臺機器上,導致作業頻繁失敗。

針對機器故障問題解決方法:

  • 針對磁碟問題,TaskManager 增加 DiskChecker 磁碟健康檢查,發現磁碟有問題 TaskManager 自動退出;
  • 針對有些機器頻繁出現 TaskManager 出現問題,根據一定的策略將有問題機器加到黑名單中,然後通過軟黑名單機制,告知 Yarn 儘量不要排程 Container 到該機器。

3.平臺化建設

3.1 平臺建設:

_26

快手的平臺化建設主要體現在青藤作業託管平臺。通過該平臺可進行作業操作、作業管理以及作業詳情檢視等。作業操作包括提交、停止作業。作業管理包括管理作業存活、效能報警,自動拉起配置等;詳情檢視,包括檢視作業的各類 Metric 等。

上圖為青藤作業託管平臺的一些操作介面。

3.2 問題定位流程優化:

_27

我們也經常需要給業務分析作業效能問題,幫助業務 debug 一些問題,過程相對繁瑣。所以該部分我們也做了很多工作,儘量提供更多的資訊給業務,方便業務自主分析定位問題。首先,我們將所有 Metric 入 Druid,通過 Superset 可從各個維度分析作業各項指標。第二,針對 Flink 的 WebUI 做了一些完善,支援 Web 實時列印 jstack,Web DAG 為各 Vertex 增加序號,Subtask 資訊中增加各併發 SubtaskId。第三,豐富異常資訊提示,針對機器當機等特定場景資訊進行明確提示。第四,新增各種 Metric。

三.未來計劃

快手的未來規劃主要分為兩個部分:

第一,目前在建設的 Flink SQL 相關工作。因為 SQL 能夠減少使用者開發的成本,包括我們現在也在對接實時數倉的需求,所以 Flink SQL 是我們未來計劃的重要部分之一。
第二,我們希望進行一些資源上的優化。目前業務在提作業時存在需求資源及併發預估不準確的情況,可能會過多申請資源導致資源浪費。另外如何提升整體叢集資源的利用率問題,也是接下來需要探索的問題。

相關文章