胡嘉偉 :實時計算在提升播放體驗的應用實踐

趙鈺瑩發表於2018-09-10

【IT168 專稿】本文根據胡嘉偉老師在2018年5月12日【第九屆中國資料庫技術大會】現場演講內容整理而成。

 講師簡介:

   

胡嘉偉,愛奇藝高階工程師。2016年畢業於上海交通大學並加入愛奇藝分散式實時計算團隊, 工作期間,作為核心開發人員, 開發實現了Babel這一公司級的統一大資料平臺, 所主導開發的Streaming SQL極大地降低了實時任務的開發門檻, 在公司重要業務得到了廣泛應用. 除了大資料平臺建設之外, 還主力參與愛奇藝使用者播放體驗實時監控與播放故障根因分析專案。

摘要:

提供正版、高清、流暢的視訊播放服務始終是愛奇藝所追求的目標, 除了播放體系本身的建設之外, 愛奇藝也立足於使用者,從使用者視角對愛奇藝播放時的播放故障、 卡頓等指標進行實時分析,以提供立體的、多維度的實時資料監控。

分享大綱:

1、使用者播放體驗

2、挑戰與架構

3、大資料平臺

4、資料探勘與分析

5、總結 & 展望

正文: 

1、使用者播放體驗

本文主要介紹愛奇藝如何使用大資料及資料探勘技術提升使用者播放體驗,此處的播放體驗指使用者在觀看視訊過程中對播放服務的體驗,而不是對播放內容的體驗。目前,我們使用的是客觀服務質量指標對使用者播放體驗進行描述,比如,卡頓比故障率等。為了能夠提供優質的播放服務,愛奇藝在播放體系建設方面投入了大量資源,比如,建立龐大的CDN網路。但是,視訊播放是一個比較複雜的過程,它會呼叫非常多後臺服務且呼叫鏈較長,同時,使用者播放環境非常複雜,它會受到網路環境、本地硬體環境以及客戶端環境的影響,任何一個環節發生問題都有可能導致使用者播放體驗下降。

過去,我們主要通過客服收集使用者反饋資訊感知服務體驗下降,當反饋量累積到一定程度時,客服人員會把資訊提交給技術人員,這個過程通常會耗費幾個小時。當技術人員接收到客服反饋資訊後,他們通常會分析日誌資料或者報表資料以進行測試排障,通常,客服提供的資訊非常有限,我們在思考如何更迅速地提供更豐富的資訊讓技術人員快速定位和解決問題。在這樣的背景下,我們啟動了使用者播放體驗實時監控及故障根因分析專案。

2、挑戰與架構

在專案開發過程中,我們主要面臨以下幾大難點:一是播放服務環境複雜,依賴子模組較多且呼叫鏈很長,任何一個環節出現問題都會導致使用者播放體驗下降,我們需要設計能夠全面描述使用者播放體驗的指標。

 

二是播放日誌資料量龐大,日新增資料量在5T以上且資料維度眾多,公共維度就有30多維,不同場景還存在特有維度。此外,很多指標需要進行復雜聯合查詢才能計算,我們需要支撐這樣一個大資料複雜計算場景。

三是指標會隨時間而變化,這些指標有明顯的以天為週期的波峰波谷特徵。此外,指標本身也是持續變化的,一些特定事件的發生也可能帶動指標變化。在這種情況下,如果採用傳統的設定閾值方式進行監控,只能對較粗粒度指標進行監控,無法對大量指標進行監控。而且需要持續調整閾值,這意味著需要耗費大量人力。

對於上述問題,我們認為頂層設計非常重要,從資料準備到資料投遞,再到資料計算,都需要進行設計規劃。為了能夠多維度全方位對播放服務質量與使用者播放體驗進行表徵,我們從使用者網路環境、使用者播放狀況、播放子模組資訊等角度入手,對20多個維度條件下的多指標進行計算,以此來描述使用者播放情況。

針對第二大難點,我們使用大資料技術獲得足夠的計算能力,以下為大家介紹愛奇藝內部的兩大平臺:大資料開發平臺和日誌資料收集平臺。依託這兩大平臺,我們可以進行資料探勘,對實時指標進行異常檢測和根因分析。

 

 

上圖為愛奇藝播放資料整體架構,資料從客戶端的播放核心投遞到後臺日誌收集叢集,日誌收集叢集在收集到日誌後會有兩條鏈路對資料進行處理:離線鏈路和實時鏈路。在離線鏈路裡,我們最終處理完的資料會儲存到Hive,或者Impala+Parquet儲存結構中,離線鏈路的資料特徵可靠穩定,缺點是延遲較大。在實時鏈路中,我們通過Spark或者Flink計算實時採集到的日誌資料,最終儲存到Impala+Kudu儲存結構中。

至於為什麼離線資料存到Impala+Parquet,而實時資料存到Impala+Kudu,是因為Kudu支援即插即查,底層可自維護資料儲存,沒有小檔案困擾,比較適合實時資料儲存;相對於Kudu,Parquet的批量查詢效能高一個數量級,離線需要保留長期歷史資料,將這些資料存到Parquet裡可提升查詢效能。

為了理解使用者的播放行為及播放情況,我們做了大量離線報表和實時報表,開發了報表資料查詢引擎,查詢時會按需對離線報表及實時報表資料進行查詢,並對查詢結果進行融合。同時,對於實時資料進行異常檢測與根因分析。

此外,我們開發了AD-HOC查詢引擎,主要解決報表資料的維度膨脹問題。通過AD-HOC查詢引擎對維度自由組合的資料進行特別支援,滿足業務對細粒度資料查詢的需求。

 

上圖為愛奇藝該技術選型中使用的開源工具,Kafka叢集經驗證可較平穩地支撐千萬級QPS實時資料量,我們基於Spark進行了很多開發,自研StreamingSQL以降低實時計算任務的開發門檻;對於愛奇藝內部的實時計算任務,我們已經不建議業務使用Storm,建議使用Spark或Flink;比如,安全風控這種對實時性要求較高的業務,基本都在使用Flink;使用Impala的原因是能夠支援複雜查詢,也可無縫支援Kudu。

基於上述開源工具,我們開發了大資料平臺Babel,提升了開發、託管以及運維任務的效率;日誌收集平臺Venus,主要作用是採集日誌資料並提供給下游計算使用。

3、大資料平臺

首先介紹大資料開發平臺Babel,Babel主要包括四大模組:任務開發、任務運維、後設資料中心以及資料交換。任務開發模組,Babel支援對批處理以及實時任務的開發,開發模式有兩種:SQL模式和Jar模式,SQL模式就是使用者通過編寫SQL制定任務,直接在網頁提交任務執行。其中,批處理SQL運算底層使用Hive,實時計算SQL底層使用的是我們自研的StreamingSQL。對於批處理任務,我們還有一個工作流的概念,主要作用是指定各批處理任務之間的依賴及排程關係,以此來維護任務。

任務運維模組,Babel的主要功能是當任務失敗時進行重試。此外,需要對實時任務狀態進行監控,比如,發生延遲或者重啟事件需要通知到使用者進行處理,不然會影響到下游計算。後設資料中心模組,主要對資料來源註冊、釋出及檢索,進行資料許可權以及血緣依賴管理。資料交換模組,實現各資料來源之間的互通。

接下來主要介紹StreamingSQL,StreamingSQL主要的使用場景是編寫SQL,通過編寫SQL描述實時ETL及實時報表計算,支援的時間模式有處理時間以及事件時間兩種,目前支援的輸出資料有Hive、MySQL和Kudu,主要是在SparkSQL基礎上對SQL語法進行擴充套件,引入新的關鍵詞,比如定義流表、維度表、臨時表、結果表以及自定義函式,維度表指靜態表,可用於與流資料做Join ,臨時表用來定義計算的中間狀態,結果表用來定義輸出資料來源資訊。我們現在支援兩種輸出模式:Append 模式和Upsert 模式。Append模式支援所有輸出資料來源;由於輸出資料更新操作最終會push down到資料來源裡進行操作,因此,如果資料來源本身支援該操作,那麼Upsert 模式也支援。

 

以上是列印資料延遲示例,首先建立一個流表,定義資料來源於哪個Kafka以及如何解碼;其次,定義中間表把資料裡的TimeStamp欄位從毫秒值轉化成秒值;然後,建立結果表定義;最後,計算結果會在控制檯進行列印,將計算邏輯插入結果表。執行該任務時,我們可以在控制檯看到這個微批次裡資料的最大值、最小值、平均值以及分位數等。

 

以上是線上例項,在實際使用中光寫業務邏輯是不夠的,還要定義環境變數、資源使用、許可權以及引數等資訊,對於複雜計算,我們可能還需要引用自定義函式。

 

以上是脫敏後的實際業務邏輯,可以看出原來需要大量程式碼實現的邏輯,現在就像使用Hive一樣寫個SQL就可以了。

 

接下來介紹日誌收集平臺 Venus,Venus的主要作用是採集日誌資料提供給下游計算使用,分為實時鏈路與離線鏈路。目前,支援的實時鏈路資料量已達到千萬級QPS,上圖為其整體流程示意圖。在二級資料上,無論是Kafka還是Hive都屬於結構化資料。在實時鏈路上,最終的二級Kafka資料延遲基本在秒級,正常情況是一到兩秒,有時會上升到六到七秒。

4、資料探勘與分析

在具體的專案開發中,資料探勘與分析主要分為以下四部分:資料計算、異常檢測、根因分析以及Ad-hoc查詢。資料計算層面,本文只介紹與實時計算相關部分,我個人認為實時計算對資料質量進行監控非常重要,尤其是目前最終的計算結果需要傳到下游進行異常檢測,如果資料本身存在問題,就會導致下游系統受到影響,最終檢測結果可能就是錯誤的。為了避免這種情況發生,我們需要對細粒度指標監控疑似異常點,對疑似異常點進行根因分析,歸併相同原因導致的故障報警,根據不同的異常特性補充對應的詳細維度資訊。最後,我們提供Ad-hoc 查詢功能,可以對細粒度資料進行查詢。

 

在實時資料計算部分,我們經過了一次架構演變,由原來的二級Kafka資料先通過StreamingSQL落盤到Hive,再以批次形式進行計算獲得最終報表資料的架構改為Lambda架構,最終資料分為實時部分以及離線部分,實時資料鏈路中使用Flink做了一次ETL,中間資料由Kafka進行中轉。

如果將上述兩個方案進行對比,原方案的架構非常簡單且節省自身計算資源,因為離線計算與實時計算共享同一份資料來源。但是,該方案存在一個致命的問題,它有可能存在資料丟失的風險,如果落盤Hive的程式發生不可恢復的故障,很可能會產生資料丟失。此外,該方案的資料延遲也比較高,整套流程走下來可能需要七至八分鐘。

新方案採用Lambda架構保證最終的資料一致性,並且由於全程都是實時計算,所以計算延遲比較低,但是新架構的整個系統更加複雜,計算鏈路、資料查詢也變得比較複雜。

 

改用新架構之後,我們也遇到了一些新的問題,一是實時計算中的反刷量問題,刷量就是指惡意日誌投遞。實際上並沒有故障發生,但惡意投遞的故障日誌資料導致最終計算獲得的故障相關指標上升,這種由於刷量導致的報警就很致命,業務根據報警資訊排障也是白白浪費時間。因此,我們的資料一定要做反刷量處理。

在過去,假設資料落盤到Hive後以五分鐘的粒度對資料進行分析,然後把其中的刷量資料過濾掉再進行計算,那麼,最終結果就是進行過反刷量的結果。改為實時計算之後,如果還是按照原來的思路就會遇到一個問題,當你識別出某些資料是刷量資料時,這條資料很可能已經被下游計算統計到最終指標結果裡了,我們的解決方案是使用Flink做一層ETL,使用滑動視窗對資料進行分析,把刷量資料特徵找出來之後存到外部資料來源,與此同時,我們在Flink內部對資料進行了迴流,讓實際資料延遲30秒之後再發到下游計算任務,這樣我們就人為製造了整個資料鏈路裡的時間差,當下遊業務接收到這些資料時,我們根據外部資料來源內的刷量資料特徵,就能夠進行正確的反刷量過濾。

我們遇到的另一個問題是,在實際計算中,必須要用事件時間處理,最終的結果才有意義,才能夠避免資料亂序等問題。但是,這樣就要求判斷資料到達情況,因為計算出來的指標要直接觸發後續的異常檢測邏輯,如果資料只是部分到達就觸發了異常檢測,那麼這種資料屬於髒資料,會導致檢測結果出現問題,比較簡單的方法是直接通過watermark 判斷資料有沒有完全到達,但這種方式很不靈活,如果watermark設定的值太大,資料延遲其實並沒有那麼大,你也必須等待那麼長的時間才能觸發後續計算;反之,如果你的watermark設定較小,那麼高峰時的資料延遲增長會很大,這可能導致大量時間段的資料不可用,這種情況也是無法接受的,所以設定一個合理的watermark值非常困難,應該如何解決呢? 

因為底層實際執行的是Spark微批次模型,所以我們可以拿到微批次資料延遲情況。根據微批次資料延遲情況和watermark分析決定是否要觸發後續計算。當資料延遲較小時,我們可以根據微批次裡的資料延遲情況及歷史資料裡的延遲情況來判斷,如果當前時間段內的資料已經完全到達,就可以直接觸發後續計算,而不用等待watermark到期再計算。 如果資料延遲較大,我們會設定watermark的最大等待時間,當資料延遲超過最大等待時間,對應時間段的資料被標記為不可用。整個流計算的每一個環節都會對延時情況進行監控。

 

第三個問題是使用Lambda架構之後,資料查詢變得更加複雜。上圖為資料查詢示例,在實際的資料查詢中,我們需要根據檢索的時間範圍對離線和實時資料進行融合,然後對最終結果進行反饋。資料到齊監控是指如果只有部分實時資料到達,肯定不可以參與後續計算。在上述示意圖中,橘色部分表示資料延遲過大或者任務發生重啟,由於無法保證資料完全正確,因此我們會對資料進行標記,通知資料查詢方,這個時間段內的資料是不可信的。

接下來是異常檢測及根因分析模組,這部分與業務耦合較強。除了實時指標需要異常檢測,我們也曾嘗試過人工設定固定閾值的方法,以此來進行監控。但是,我們很快就發現這些指標的特性一直在變化,這些閾值也要跟著不斷調整,這個過程非常耗費人力,而且也沒有辦法對太多指標進行監控,只能對少量指標監控。之後,我們嘗試使用機器學習的方法進行異常檢測,我們嘗試過很多方法,總結來說主要有以下兩大套路:

一是假設資料服從某種單峰分佈,但我們的資料實際上並不是單峰分佈,很可能是多峰分佈,因此這種方法並不適用。二是基於預測進行異常檢測,主要是通過計算某一種約束條件下的期望來做預測,根據期望值與實際值的偏差判斷是否有異常。然而,我們更希望能夠計算不確定性的值,通過不確定性的值的大小進行判斷。如果希望基於預測的異常檢測達到更好的效果,需要對指標進行預處理,將指標對映為平穩序列,但是這種做法會耗費大量人力。

針對我們的業務場景,我們的第一步是引入時域特徵,模型能夠捕獲一天中不同時間段的曲線特徵,並且通過週期性地訓練模型,使用歷史資料訓練模型不斷更新引數,模型引數是不斷動態調整的。

第二步是計算曲線波動以及偏移特徵,主要使用概率分佈估計以及CUSUM,這兩個量作為基礎特徵用於後續的異常檢測邏輯中。

第三步是消除異常資料影響,如果我們已經判斷出某個時刻的資料有問題,那麼在後續檢測中就要把該時刻的資料取消,我們用Holt-Winters對該時間點的資料進行擬合,避免負面影響。

第四步也是最重要的一點,我們引入了視覺特徵做模式識別。因為很多機器學習模型檢測出的異常,人工檢視資料時會覺得不夠顯著,認為不是異常。我們通過模式識別主要使用第二步計算得到的特徵值作為輸入檢測出人們在視覺上能夠認可的異常曲線。最終我們達到了對3000多個指標進行實時監控,整體延遲控制在兩分鐘左右。

在檢測出疑似異常點之後,我們需要做根因分析,我們的根因分析主要基於專家系統,分為合併、歸因及補充三步,由於異常點檢測是對細粒度下的指標進行檢測,那麼就會有這樣一個情況,比如,某運營商某地域服務發生問題導致產生多個疑似異常點,這些異常點的特徵是運營商條件一樣,但是具體省份資訊不一樣,那麼,我們根據這兩個資訊對疑似異常點進行合併,把由於某運營商某地域故障導致的異常合併為一個異常點,在合併之後進行歸因,尋找具體原因,我們會在實時原始資料裡查詢對應條件下增量最大的故障碼,根據故障碼判斷導致問題發生的真實原因。最後根據故障條件與故障碼,從實時原始資料中查詢聚合業務特定的資訊,加入最終的故障報告中,並將報告下發給對應的業務人員。

此外,我們也提供Ad-hoc查詢,主要解決資料多維膨脹問題,目前我們能夠支援20多個維度任意組合查詢,並且可以對結果進行視覺化,對查詢結果進行分析和對比,幫助業務人員更好得分析資料。

5、總結 & 展望

總結來看,頂層設計以及合理設定有意義的指標是最重要的。如果這些指標本身意義不大,那麼後面的計算也無法產出比較有用的資訊。此外,可靠的平臺也很重要,提升了開發效率,保障了任務穩定執行。我們在大資料實時計算部分主要使用了 Kafka、Flink、StreamingSQL以及Impala等元件,通過基於機器學習的異常檢測減少人工干預,設計了基於專家系統的根因分析並開發了資料查詢引擎。

未來,我們將增強StreamingSQL 功能,使其能夠支援輸出到Kafka以及ES這樣的資料來源;調研Kafka1.0以上版本,希望能夠真正做到at-least-once;增強資料質量監控,希望達到對資料裡面具體值分佈情況的監控;考慮不同業務特點的異常檢測,讓異常檢測對業務更加敏感;將故障日誌與業務其他日誌進行關聯,獲得更加完善的故障報告。

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

相關文章