1. 程式人生 > >【架構】分散式追蹤系統設計與實現

【架構】分散式追蹤系統設計與實現

分散式系統為什麼需要 Tracing?

  先介紹一個概念:分散式跟蹤,或分散式追蹤

  電商平臺由數以百計的分散式服務構成,每一個請求路由過來後,會經過多個業務系統並留下足跡,併產生對各種Cache或DB的訪問,但是這些分散的資料對於問題排查,或是流程優化都幫助有限。對於這麼一個跨程序/跨執行緒的場景,彙總收集並分析海量日誌就顯得尤為重要。要能做到追蹤每個請求的完整呼叫鏈路,收集呼叫鏈路上每個服務的效能資料,計算效能資料和比對效能指標(SLA),甚至在更遠的未來能夠再反饋到服務治理中,那麼這就是分散式跟蹤的目標了。在業界,twitter 的 zipkin 和淘寶的鷹眼就是類似的系統,它們都起源於 Google Dapper 論文,就像歷史上 Hadoop 發源於 Google Map/Reduce 論文,HBase 源自 Google BigTable 論文一樣。

  好了,整理一下,Google叫Dapper,淘寶叫鷹眼,Twitter叫ZipKin,京東商城叫Hydra,eBay叫Centralized Activity Logging (CAL),大眾點評網叫CAT,我們叫Tracing。

  這樣的系統通常有幾個設計目標:

(1)低侵入性——作為非業務元件,應當儘可能少侵入或者無侵入其他業務系統,對於使用方透明,減少開發人員的負擔;

(2)靈活的應用策略——可以(最好隨時)決定所收集資料的範圍和粒度;

(3)時效性——從資料的收集和產生,到資料計算和處理,再到最終展現,都要求儘可能快;

(4)決策支援——這些資料是否能在決策支援層面發揮作用,特別是從 DevOps 的角度;

(5)視覺化才是王道。

 

先來一個直觀感受:

  下面依次展示了 ZipKin、鷹眼、窩窩的呼叫鏈繪製介面。

圖1 twitter zipkin 呼叫鏈

圖2 淘寶鷹眼的呼叫鏈

圖3 京東商城hydra呼叫鏈

圖4 窩窩tracing呼叫鏈

 

  滑鼠移動到呼叫鏈的每一層點選,可以看到執行時長、宿主機IP、資料庫操作、傳入引數甚至錯誤堆疊等等具體資訊。

 


淘寶如何實現的:

  同一次請求的所有相關呼叫的情況,在淘寶 EagleEye 裡稱作 呼叫鏈

。同一個時刻某一臺伺服器並行發起的網路呼叫有很多,怎麼識別這個呼叫是屬於哪個呼叫鏈的呢?可以在各個發起網路呼叫的中介軟體上下手。

  在前端請求到達伺服器時,應用容器在執行實際業務處理之前,會先執行 EagleEye 的埋點邏輯(類似 Filter 的機制),埋點邏輯為這個前端請求分配一個全域性唯一的呼叫鏈ID。這個ID在 EagleEye 裡面被稱為 TraceId,埋點邏輯把 TraceId 放在一個呼叫上下文物件裡面,而呼叫上下文物件會儲存在 ThreadLocal 裡面。呼叫上下文裡還有一個ID非常重要,在 EagleEye 裡面被稱作 RpcId。RpcId 用於區分同一個呼叫鏈下的多個網路呼叫的發生順序和巢狀層次關係。對於前端收到請求,生成的 RpcId 固定都是0。

  當這個前端執行業務處理需要發起 RPC 呼叫時,淘寶的 RPC 呼叫客戶端 HSF 會首先從當前執行緒 ThreadLocal 上面獲取之前 EagleEye 設定的呼叫上下文。然後,把 RpcId 遞增一個序號。在 EagleEye 裡使用多級序號來表示 RpcId,比如前端剛接到請求之後的 RpcId 是0,那麼 它第一次呼叫 RPC 服務A時,會把 RpcId 改成 0.1。之後,呼叫上下文會作為附件隨這次請求一起傳送到遠端的 HSF 伺服器。

  HSF 服務端收到這個請求之後,會從請求附件裡取出呼叫上下文,並放到當前執行緒 ThreadLocal 上面。如果服務A在處理時,需要呼叫另一個服務,這個時候它會重複之前提到的操作,唯一的差別就是 RpcId 會先改成 0.1.1 再傳過去。服務A的邏輯全部處理完畢之後,HSF 在返回響應物件之前,會把這次呼叫情況以及 TraceId、RpcId 都列印到它的訪問日誌之中,同時,會從 ThreadLocal 清理掉呼叫上下文。如圖6-1展示了一個瀏覽器請求可能觸發的系統間呼叫。

圖6-1-一個瀏覽器請求可能觸發的系統間呼叫

  圖6-1描述了 EagleEye 在一個非常簡單的分散式呼叫場景裡做的事情,就是為每次呼叫分配 TraceId、RpcId,放在 ThreadLocal 的呼叫上下文上面,呼叫結束的時候,把 TraceId、RpcId 列印到訪問日誌。類似的其他網路呼叫中介軟體的呼叫過程也都比較類似,這裡不再贅述了。訪問日誌裡面,一般會記錄呼叫時間、遠端IP地址、結果狀態碼、呼叫耗時之類,也會記錄與這次呼叫型別相關的一些資訊,如URL、服 務名、訊息topic等。很多呼叫場景會比上面說的完全同步的呼叫更為複雜,比如會遇到非同步、單向、廣播、併發、批處理等等,這時候需要妥善處理好 ThreadLocal 上的呼叫上下文,避免呼叫上下文混亂和無法正確釋放。另外,採用多級序號的 RpcId 設計方案會比單級序號遞增更容易準確還原當時的呼叫情況。

  最後,EagleEye 分析系統把呼叫鏈相關的所有訪問日誌都收集上來,按 TraceId 彙總在一起之後,就可以準確還原呼叫當時的情況了。

圖6-2-一個典型的呼叫鏈

  如圖6-2所示,就是採集自淘寶線上環境的某一條實際呼叫鏈。呼叫鏈通過樹形展現了呼叫情況。呼叫鏈可以清晰地看到當前請求的呼叫情況,幫助問題定 位。如上圖,mtop應用發生錯誤時,在呼叫鏈上可以直接看出這是因為第四層的一個([email protected])請求導致網路超時,使最上層頁面出現超時問題。這種呼叫鏈,可以在 EagleEye 系統監測到包含異常的訪問日誌後,把當前的錯誤與整個呼叫鏈關聯起來。問題排查人員在發現入口錯誤量上漲或耗時上升時,通過  EagleEye 查找出這種包含錯誤的呼叫鏈取樣,提高故障定位速度。

呼叫鏈資料在容量規劃和穩定性方面的分析

  如果對同一個前端入口的多條呼叫鏈做彙總統計,也就是說,把這個入口URL下面的所有呼叫按照呼叫鏈的樹形結構全部疊加在一起,就可以得到一個新的樹結構(如圖6-3所示)。這就是入口下面的所有依賴的呼叫路徑情況。

圖6-3-對某個入口的呼叫鏈做統計之後得到的依賴分析

  這種分析能力對於複雜的分散式環境的呼叫關係梳理尤為重要。傳統的呼叫統計日誌是按固定時間視窗預先做了統計的日誌,上面缺少了鏈路細節導致沒辦法對超過兩層以上的呼叫情況進行分析。例如,後端資料庫就無法評估資料庫訪問是來源於最上層的哪些入口;每個前端系統也無法清楚確定當前入口由於雙十一活動流量翻倍,會對後端哪些系統造成多大的壓力,需要分別準備多少機器。有了 EagleEye 的資料,這些問題就迎刃而解了。

  下圖6-4展示了資料流轉過程。

圖6-4 鷹眼的資料收集和儲存

 

京東如何實現的: 

  京東商城引入了阿里開源的服務治理中介軟體 Dubbo,所以它的分散式跟蹤 Hydra 基於 Dubbo 就能做到對業務系統幾乎無侵入了。

  Hydra 的領域模型如下圖7所示:

圖7 hydra 領域模型以及解釋

  hydra 資料儲存是 HBase,如下圖8所示:

圖8 hydra 架構

 

窩窩如何實現的: 

  2012年,逐漸看到自建分散式跟蹤系統的重要性,但隨即意識到如果沒有對 RPC 呼叫框架做統一封裝,就可能侵入到每一個業務工程裡去寫埋點日誌,於是推廣 Dubbo 也提上日程。2013年,確定系統建設目標,開始動手。由於 tracing 跟 DevOps 息息相關,所以資料聚合、儲存、分析和展示由運維部向榮牽頭開發,各個業務工程資料埋點和上報由研發部國璽負責。

  經過後續向榮、劉卓、國璽、明斌等人的不斷改進,技術選型大致如下所示。

  • 埋點
    • 實現執行緒內 trace 上下文傳遞,即伺服器內部的方法互調時不需要強制在方法形參中加 Message 引數;
    • 實現 trace 埋點邏輯自動織入功能,即業務開發人員不需要在方法中列印 trace 日誌,只需要給該方法加註解標識 ;
    • 原理:
      • 利用 Javaagent 機制,執行 main 方法之前,會先執行 premain 方法,在該方法中將位元組碼轉換器載入 instrumentation,而後 jvm 在載入 class 檔案之前都會先執行位元組碼轉換器。
      • 位元組碼轉換器中的邏輯為,識別出注解 trace 的類及方法,並修改該方法位元組碼,織入埋點邏輯。進入方法時會初始 trace 上下文資訊,並存儲線上程的 threadLocals 中,退出方法會列印 trace 日誌並清空該方法的上下文。
  • 資料聚合
    • 應用層 trace 日誌通過 flume agents 實時傳送至 flume collector;
  • 資料儲存
    • 服務端分別通過 hdfs-sink 和 hbase-sink,實時錄入至 hbase、hdfs;
    • hdfs 有 tmp 臨時檔案存放實時聚合過來的資料,每5分鐘生成一個 done 檔案;
  • 資料分析和統計
    • load 程式每 4 分鐘檢查 done 檔案並存放至 hive 表 hkymessage 指定分割槽;
    • 分析程式每5分鐘執行一次, 將生成統計資料入庫, 結果集資料如下:
      資料格式:{5個分層的5個響應時段請求個數合集}   {5個分層5-10s和大於10s散點資料合集}  當前5分鐘最後一次請求rootid  統計時間
  • 資料展示
    • 基於 Python 的 Django

基於這些資料分析和統計,我們就能繪製效能曲線圖,從中可以發現哪些時間點哪些層有效能問題,然後一路點進去,直到找到到底是哪一個呼叫鏈裡的哪一個環節慢。

 

圖9 效能曲線預設圖形

  

  還可以從每一次呼叫結果分析出各層的異常曲線,並按照 memcached/redis/mongodb/mysql/runtime/fail 分類檢視。

 

圖10 異常曲線預設圖形

  

  還可以進一步統計各個業務工程的訪問量、訪問質量和平均訪問時長,並於歷史同期對比,從而快速理解系統服務質量。

 

  如上所述,窩窩的 Tracing(鷹眼) 系統目前已投入使用,歸併在 OAP(運維自動化平臺)裡。

 

-over-