分布式跟踪系统
2019獨角獸企業重金招聘Python工程師標準>>>
分布式跟蹤系統 博客分類: 架構 java 開源框架 分布式系統為什么需要 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應用發生錯誤時,在調用鏈上可以直接看出這是因為第四層的一個(tair@1)請求導致網絡超時,使最上層頁面出現超時問題。這種調 用鏈,可以在 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
轉載于:https://my.oschina.net/xiaominmin/blog/1599173
總結
- 上一篇: etcd集群部署与遇到的坑
- 下一篇: C# OO(初级思想)