背景
阿裡集團針對故障處理提出了“1/5/10”的目标-- 1 分鐘發現、5 分鐘定位、10 分鐘恢複,這對我們的定位能力提出了更高的要求。
EMonitor 是一款內建 Tracing 和 Metrics,服務于餓了麼所有技術部門的一站式監控系統,其覆寫了
- 前端監控、接入層監控;
- 業務 Trace 和 Metric 監控;
- 所有的中間件監控;
- 容器監控、實體機監控、機房網絡監控。
每日處理總資料量近 PB,每日寫入名額資料量上百 T,日均幾千萬的名額查詢量,内含上萬個圖表、數千個名額看闆,并且已經将所有層的監控資料打通并串聯了起來。但是在故障來臨時,使用者仍然需要花費大量時間來檢視和分析 EMonitor 上的資料
比如阿裡本地生活的下單業務,涉及到諸多應用,每個應用諸多 SOA 服務之間錯綜複雜的調用關系,每個應用還依賴 DB、Redis、MQ 等等資源,在下單成功率下跌時,這些應用的負責人都要在 EMonitor 上檢視名額曲線以及鍊路資訊來進行人肉排障以自證清白,耗時耗力,是以自動化的根因分析必不可少。
根因分析模組化
業界已經有好多在做根因分析的了,但是大都準确率不高,大部分還在 40% 到 70% 之間,從側面說明根因分析确實是一個難啃的骨頭。
根因分析看起來很龐大,很抽象,無從下手,從不同的角度(可能是表象)去看它,就可能走向不同的路。那如何才能透過表象來看到本質呢?
我這裡并不會一開始就給你列出高大上的算法解決方案,而是要帶你重新認知根因分析要解決的問題到底是什麼。其實好多人對要解決的問題都模糊不清,你隻有對問題了解清楚了,才能有更好的方案來解決它。
要解決什麼樣的問題
舉個例子:現有一個應用,擁有一堆容器資源,對外提供了諸多 SOA 服務或者 Http 服務,同時它也依賴了其他應用提供的服務,以及 DB 資源、Redis 資源、MQ 資源等等;那我們如何才能夠全面的掌控這個應用的健康狀況呢?
我們需要掌控:
- 掌控這個應用的所有入口服務的「耗時」和「狀态」
- 掌控每個入口服務之後每種操作的「耗時」和「狀态」
一個應用都有哪些入口?
- SOA 服務入口;
- Http 服務入口;
- MQ 消費消息入口;
- 定時 job 入口;
- 其他的一些入口。
進入每個入口之後,都可能會有一系列的如下 5 種操作和 1 種行為(下面的操作屬性都是以阿裡本地生活的實際情況為例,并且都包含所在機房的屬性):
- DB 遠端操作:有 dal group、table、operation(比如select、update、insert等)、sql 的操作屬性;
- Redis 遠端操作:有 command 的操作屬性;
- MQ 遠端操作(向MQ中寫消息):有 exchange、routingKey、vhost 的操作屬性;
- RPC 遠端操作:有 遠端依賴的 appId、遠端 method 的操作屬性;
- Local 操作(即除了上述4種遠端操作之外的本地操作): 暫無屬性;
- 抛出異常的行為:有異常 name 的屬性。
那我們其實就是要去統計每個入口之後的 5 種操作的耗時情況以及狀态情況,以及抛出異常的統計情況。
針對遠端操作其實還要明細化,上述遠端操作的每一次耗時是包含如下 3 大部分:
- 用戶端建立連接配接、發送請求和接收響應的耗時;
- 網絡的耗時;
- 伺服器端執行的耗時。
有了上述三要素,才能去确定遠端操作到底是哪出了問題,不過實際情況可能并沒有上述三要素。
故障的結論
有了上述資料的全面掌控,當一個故障來臨的時候,我們可以給出什麼樣的結論?
- 哪些入口受到影響了?
- 受影響的入口的本地操作受到影響了?
- 受影響的入口的哪些遠端操作受到影響了?
- 具體是哪些遠端操作屬性受到影響了?
- 是用戶端到伺服器端的網絡受到影響了?
- 是伺服器端出了問題嗎?
- 受影響的入口都抛出了哪些異常?
上述的這些結論是可以做到非常高的準确性的,因為他們都是可以用資料來證明的。
然而第二類根因,卻是無法證明的:
- GC 問題;
- 容器問題。
他們一旦出問題,更多的表現是讓上述 5 種操作耗時變長,并且是沒法給出資料來明确證明他們和 5 種操作之間的關聯,隻是以一種推測的方式來懷疑,從理論上來說這裡就會存在誤定位的情況。
還有第三類更加無法證明的根因:
- 變更問題
昨天的變更或者目前的變更到底和目前的故障之間有沒有關聯,更是無法給出一個證明的,是以也隻能是瞎推測。
我們可以明确的是 5 種操作的根因,還需要進一步去檢視是否是上述第二類根因或者第三類根因,他們隻能作為一些輔助結論,因為沒法給出嚴謹的資料證明。
根因分析實作
在明确了我們要解決的問題以及要求的故障結論之後,我們要采取什麼樣的方案來解決呢?下面首先會介紹一個基本功能「名額下鑽分析」。
名額下鑽分析
一個名額,有多個 tag,當該名額總體波動時,名額下鑽分析能夠幫你分析出是哪些 tag 組合導緻的波動。
比如用戶端的 DB 操作抖動了,利用名額下鑽分析就可以分析出
- 哪個機房抖動了?
- 哪個 dal group 抖動了?
- 哪張表抖動了?
- 哪種操作抖動了?
- 哪個 sql 抖動了?
再比如遠端 RPC 操作抖動了,利用名額下鑽分析就可以分析出
- 哪個遠端 appId 抖動了?
- 哪個遠端 method 抖動了?
其實這個就是去年 AIOPS 競賽的題目,詳細見:
http://iops.ai/competition_detail/?competition_id=8&flag=1通常的方案:
- 對每個時序曲線都要進行資料預測,拿到預測值;
- 針對每個方案根據實際值和預測值的差異算出一個方案分數;
- 周遊所有可能性方案,挑選出分數最高的方案(通過蒙特卡洛樹搜尋進行剪枝優化)。
我們的方案:
- 确定整體的波動範圍;
- 确定計算範圍 = 該波動範圍 + 前面一段正常範圍,在計算範圍内算出每根時間線的波動值(比如波動的方差,實際肯定不會這麼簡單,有很多的優化點);
- 對所有時間曲線進行一些過濾(比如和整體抖動方法相反,整體都向上抖動,它向下抖動);
- 對過濾後的時間曲線按照波動值進行 KMeans 聚類;
- 從排名靠前的分類中挑選出方案,為每個方案計算方案分數(這個方案個數就非常之少了);
- 得出分數最高的方案。
針對去年的決賽題目,我們的這個方案的跑分達到了 0.95 以上,應該就在前三名了。
根因分析流程
有了名額下鑽分析功能之後,我們來看如何進行根因分析:

- 針對入口服務的響應時間和成功率判斷是否異常,若無異常則無根因可分析;
- 針對入口服務的 5 種操作進行名額下鑽分析,得出異常波動的操作類型有哪些;
- 然後到對應操作類型的名額中再次進行名額下鑽分析,得出該操作下:
- 哪些入口受到該操作的波動影響了?
- 哪些操作屬性異常波動了?
- 假如該操作是遠端操作,還要繼續深入伺服器端進行分析:
假如你有遠端操作資料的 3 要素的話,那麼是可以分析出:
- 用戶端建立連接配接、發送請求和接收響應耗時問題;
- 網絡耗時問題;
- 伺服器端耗時問題。
假如沒有相關資料的話,那就隻能相對來說進行推測了(準确率也會受到影響):
- 假如伺服器端耗時正常的話,那就相對劃分為用戶端發送請求或接收響應耗時是根因;
- 假如伺服器端耗時異常抖動的話,那麼就需要到伺服器端進行深入分析;
- 比如 DB 操作來說,可以繼續深入到 DAL 層面分析:
DAL 是我們的分庫分表的資料庫代理層,同時起到一些熔斷限流的作用,是以一條 SQL 執行時間會包含在 DAL 代理層的停留時間、以及在 DB 層的執行時間,利用名額下鑽分析,可以分析出如下的一些根因:
- 某個 table 的所有操作耗時抖動?
- 某條sql操作耗時抖動?
- 某台具體DB執行個體抖動?
- SQL的停留時間 or 執行時間抖動?
- 比如用戶端的 RPC 操作來說,可以繼續遞歸到遠端 appId 的;
- 針對受影響的這些入口使用名額下鑽分析,哪些異常也抖動了(有些異常一直在抛,但是跟本次故障無關);
- 再次檢視上述抖動的操作是否是由 GC 問題、容器問題、變更問題等引起的。
落地情況
阿裡本地生活的根因分析能力,1 個月内從産生根因分析的想法到實作方案上線到生産(不包括名額下鑽分析的實作,這個是之前就已經實作好的了),1 個月内在生産上實驗和優化并推廣開來,總共 2 個月内實作了從 0 到 1 并取得了如下成績
- 50 個詳細記載的案例中準确定位 48 次,準确率 96%;
- 最高一天執行定位 500 多次;
- 平均定位耗時 1 秒;
- 詳細的定位結果展示。
我們對定位準确性的要求如下:
- 要明确給出受到影響的入口服務有哪些;
- 每個受到影響的入口服務抖動的操作根因以及操作屬性都要正确;
每個入口服務抖動的根因很可能不一樣的,比如目前應用的 SOA1 是由于 Redis 操作抖動,目前應用的 SOA2 是由于遠端 RPC 依賴的其他 SOA3 抖動導緻,SOA3 是由于 Redis 操作抖動導緻;
- 用戶端操作耗時抖動到底是用戶端原因還是伺服器端原因要保證正确;
- 容器問題時,要保證不能定位到錯誤的容器上。
準确率為什麼這麼高?
我認為主要是以下 3 個方面:
資料的完整度
假如是基于采樣鍊路去分析,必然會存在因為漏采導緻誤判的問題。
我們分析的資料是全量鍊路資料轉化成的名額資料,不存在采樣的問題,同時在分析時可以直接基于名額進行快速分析,臨時查采樣的方式分析速度也是跟不上的。
模組化的準确性
你的模組化方案能回答出每個 SOA 服務抖動的根因分别是什麼嗎?
絕大部分的模組化方案可能都給不出嚴謹的資料證明,以 DB 是根因為例,他們的模組化可能是 SOA 服務是一個名額,DB 操作耗時是一個名額,2 者之間是沒有任何關聯的,沒有資料關聯你就給不出嚴謹的證明,即沒法證明 DB 的這點抖動跟那個 SOA 服務之間到底有沒有關聯,然後就隻能處于瞎推測的狀态,這裡就必然存在誤判的情況。
而我們上述的模組化是建立了相關的關聯,我們會統計每個入口後的每種操作的耗時,是可以給到嚴謹的資料證明。
異常判定的自适應性
比如 1 次 SOA 服務整體耗時 1s,遠端調用 RPC1 耗時 200ms,遠端調用 RPC2 耗時 500ms,到底是哪個 RPC 調用耗時抖動呢?耗時最長的嗎?超過一定門檻值的 RPC 嗎?
假如你有門檻值、同環比的限制,那麼準确率一定不高的。我們的名額下鑽分析在解決此類問題的時候,是通過目前情況下的波動貢獻度的方式來計算,即使你耗時比較高,但是和之前正常情況波動不大,那麼就不是波動的根因。
速度為什麼這麼快?
我認為主要是以下 2 方面的原因:
業内領先的時序資料庫 LinDB
根因分析需要對諸多名額的全量次元資料進行 group by 查詢,是以背後就需要依靠一個強大的分布式時序資料庫來提供實時的資料查詢能力。
LinDB 時序資料庫是我們阿裡本地生活監控系統 E-Monitor 上一階段的自研産物,在查詢方面:
- 強悍的資料壓縮:時序資料原始資料量和實際存儲量之比達到 58:1,相同 PageCache 的記憶體可以比别的系統容納更多的資料;
- 高效的索引設計:索引的預過濾,改造版的 RoaringBitmap 之間的 and or 操作來進行高效的索引過濾;
- 單機内充分并行化的查詢機制:利用 akka 架構對整個查詢流程異步化。
整體查詢效率是 InfluxDB 的幾倍到幾百倍,詳細見文章
分布式時序資料庫 - LinDB
https://zhuanlan.zhihu.com/p/35998778名額下鑽分析算法的高效
- 我們不需要每個時間線都進行預測;
- 實際要計算的方案個數非常之少;
- 方案算分上可以适應于任何加減乘除之類的名額計算上,比如根因定位中的平均響應時間 = 總時間 / 總次數
SOA1 的平均響應時間 t1 和 SOA2 的平均響應時間 t2,SOA1 和 SOA2 的總體平均響應時間并不是 t1 和 t2 的算術平均而是權重平均,如果是權重平均,那麼久需要多存儲一些額外的資訊,并且需要進行額外的權重計算
實際案例
案例 1
故障現場如下,某個應用的 SOA 服務抖動上升:
直接點選根因分析,就可以分析到如下結果
AppId1 的 SOA 服務在某個機房下抖動了
- 依賴的 AppId2 的 SOA 服務抖動
- 依賴的 AppId3 的 SOA 服務抖動
- 依賴的 AppId5 的本地處理和 Redis 操作耗時抖動
- 依賴的 AppId6 的本地處理和 Redis 操作耗時抖動
- 依賴的 AppId4 的本地處理和 Redis 操作耗時抖動
- 依賴的 AppId3 的 SOA 服務抖動
這裡的本地處理包含擷取 Redis 連接配接對象 Jedis 的耗時,這一塊沒有耗時打點就導緻劃分到本地處理上了,後續會進行優化。這裡沒有給出詳細的 Redis 叢集或者機器跟我們的實際情況有關,可以暫時忽略這一點。
點選上面的每個應用,下面的表格都會列出所點選應用的詳細故障資訊
- 受影響的 SOA 服務有哪些,比如 AppId1 的 3 個 SOA 服務受到影響;
- 每個 SOA 服務抖動的根因,比如 AppId1 的 3 個 SOA 服務都受到 AppId2 的 1 個 SOA 服務的抖動影響;
- 表格中每一個連結都可以跳轉到對應的名額曲線監控面闆上。
再比如點選 AppId4,顯示如下:
AppId4 的 1 個 SOA 方法抖動
- 該方法的本地處理抖動(實際是擷取 Redis 連接配接對象 Jedis 的耗時抖動);
- 該方法依賴的 Redis 操作抖動;
- 該方法抛出 Redis 連接配接異常;
案例2
故障現場如下,某個應用的 SOA 服務抖動上升
點選根因分析,就可以幫你分析到如下結果
-
- 依賴的 DB 服務抖動
-
-
點選 AppId2,可以看下詳細情況,如下所示:
從表格中就可以看到,根因是 DB 的一台執行個體抖動導緻這個 dal group 所有操作抖動。
作者
李剛,網名乒乓狂魔,餓了麼監控組研發專家,餓了麼内部時序資料庫 LinDB 的項目負責人,餓了麼根因分析項目負責人,目前緻力于監控的智能分析領域以及下一代全景監控的體系化建構;
林濱(予譜),餓了麼監控組前端工程師,現負責一站式監控系統 EMonitor 的前端開發,旨在将繁雜的資料以高可視化輸出。