分布式技術原理與實戰(zhàn)45講--第15講:如何實現(xiàn)分布式調(diào)用跟蹤
分布式服務拆分以后,系統(tǒng)變得日趨復雜,業(yè)務的調(diào)用鏈也越來越長,如何快速定位線上故障,就需要依賴分布式調(diào)用跟蹤技術。下面我們一起來看下分布式調(diào)用鏈相關的實現(xiàn)。
為什么需要分布式調(diào)用跟蹤
隨著分布式服務架構(gòu)的流行,特別是微服務等設計理念在系統(tǒng)中的應用,系統(tǒng)架構(gòu)變得越來越分散,如下圖所示。

可以看到,隨著服務的拆分,系統(tǒng)的模塊變得越來越多,不同的模塊可能由不同的團隊維護,一個請求可能會涉及幾十個服務的協(xié)同處理, 牽扯到多個團隊的業(yè)務系統(tǒng)。
假設現(xiàn)在某次服務調(diào)用失敗,或者出現(xiàn)請求超時,需要定位具體是哪個服務引起的異常,哪個環(huán)節(jié)導致的超時,就需要去每個服務里查看日志,這樣的處理效率是非常低的。
另外,系統(tǒng)拆分以后,缺乏一個自上而下全局的調(diào)用 ID,如何有效地進行相關的數(shù)據(jù)分析工作呢?比如電商的活動轉(zhuǎn)化率、購買率、廣告系統(tǒng)的點擊鏈路等。如果沒有一個統(tǒng)一的調(diào)用 ID 來記錄,只依靠業(yè)務上的主鍵等是很難實現(xiàn)的,特別是對于一些大型網(wǎng)站系統(tǒng),如淘寶、京東等,這些問題尤其突出。
分布式調(diào)用跟蹤的業(yè)務場景
分布式調(diào)用跟蹤技術 就是解決上面的業(yè)務問題,即通過調(diào)用鏈的方式,把一次請求調(diào)用過程完整的串聯(lián)起來,這樣就實現(xiàn)了對請求調(diào)用路徑的監(jiān)控。
分布式調(diào)用鏈其實就是將一次分布式請求還原成 調(diào)用鏈路,顯式的在后端查看一次分布式請求的調(diào)用情況,比如各個節(jié)點上的耗時、請求具體打到了哪臺機器上、每個服務節(jié)點的請求狀態(tài)等。
一般來說,分布式調(diào)用跟蹤可以應用在以下的場景中。
故障快速定位:通過調(diào)用鏈跟蹤,一次請求的邏輯軌跡可以完整清晰地展示出來。在開發(fā)的過程中,可以在業(yè)務日志中添加調(diào)用鏈 ID,還可以通過調(diào)用鏈結(jié)合業(yè)務日志快速定位錯誤信息。
各個調(diào)用環(huán)節(jié)的性能分析:在調(diào)用鏈的各個環(huán)節(jié)分別添加調(diào)用時延,并分析系統(tǒng)的性能瓶頸,進行針對性的優(yōu)化。
各個調(diào)用環(huán)節(jié)的可用性,持久層依賴等:通過分析各個環(huán)節(jié)的平均時延、QPS 等信息,可以找到系統(tǒng)的薄弱環(huán)節(jié),對一些模塊做調(diào)整,比如數(shù)據(jù)冗余等。
數(shù)據(jù)分析等:調(diào)用鏈是一條完整的業(yè)務日志,可以得到用戶的行為路徑,并匯總分析。
分布式調(diào)用跟蹤實現(xiàn)原理
分布式鏈路跟蹤的技術實現(xiàn),主要是參考 Google 的 Dapper 論文,分布式調(diào)用跟蹤是一種全鏈路日志,主要的設計基于 Span 日志格式,下面簡單介紹這個日志結(jié)構(gòu)。
Dapper 用 Span 來表示一個服務調(diào)用開始和結(jié)束的時間,也就是時間區(qū)間,并記錄了 Span 的名稱以及每個 Span 的 ID 和父 ID,如果一個 Span 沒有父 ID 則被稱之為 Root Span。
一個請求到達應用后所調(diào)用的所有服務,以及所有服務組成的調(diào)用鏈就像是一個樹結(jié)構(gòu),追蹤這個調(diào)用鏈路得到的樹結(jié)構(gòu)稱之為 Trace,所有的 Span 都掛在一個特定的 Trace 上,共用一個 TraceId。

在一次 Trace 中,每個服務的每一次調(diào)用,就是一個 Span,每一個 Span 都有一個 ID 作為唯一標識。同樣,每一次 Trace 都會生成一個 TraceId 在 Span 中作為追蹤標識,另外再通過一個 parentSpanId,標明本次調(diào)用的發(fā)起者。
當 Span 有了上面三個標識后,就可以很清晰地將多個 Span 進行梳理串聯(lián),最終歸納出一條完整的跟蹤鏈路。
確定了日志格式以后,接下來日志如何采集和解析,日志的采集和存儲有許多開源的工具可以選擇。一般來說,會使用離線 + 實時的方式去存儲日志,主要是分布式日志采集的方式,典型的解決方案如 Flume 結(jié)合 Kafka 等 MQ,日志存儲到 HBase 等存儲中,接下來就可以根據(jù)需要進行相關的展示和分析。
分布式調(diào)用跟蹤的選型
大的互聯(lián)網(wǎng)公司都有自己的分布式跟蹤系統(tǒng),比如前面介紹的 Google 的 Dapper、Twitter 的 Zipkin、淘寶的鷹眼等。
Google 的 Drapper
Dapper 是 Google 生產(chǎn)環(huán)境下的分布式跟蹤系統(tǒng),沒有對外開源,但是 Google 發(fā)表了“Dapper - a Large-Scale Distributed Systems Tracing Infrastructure”論文,介紹了他們的分布式系統(tǒng)跟蹤技術,所以后來的 Zipkin 和鷹眼等都借鑒了 Dapper 的設計思想。
Twitter 的 Zipkin
Zipkin 是一款開源的分布式實時數(shù)據(jù)追蹤系統(tǒng),基于 Google Dapper 的論文設計而來,由 Twitter 公司開發(fā)貢獻。其主要功能是聚集來自各個異構(gòu)系統(tǒng)的實時監(jiān)控數(shù)據(jù),用來追蹤微服務架構(gòu)下的系統(tǒng)延時問題,Zipkin 的用戶界面可以呈現(xiàn)一幅關聯(lián)圖表,以顯示有多少被追蹤的請求通過了每一層應用。

阿里的 EagleEye
EagleEye 鷹眼系統(tǒng)是 Google 的分布式調(diào)用跟蹤系統(tǒng) Dapper 在淘寶的實現(xiàn),EagleEye 沒有開源。下面這段介紹來自 阿里中間件團隊:
前端請求到達服務器,應用容器在執(zhí)行實際業(yè)務處理之前,會先執(zhí)行 EagleEye 的埋點邏輯。埋點邏輯為這個前端請求分配一個全局唯一的調(diào)用鏈 ID,即 TraceId。埋點邏輯把 TraceId 放在一個調(diào)用上下文對象里面,而調(diào)用上下文對象會存儲在 ThreadLocal 里面。調(diào)用上下文里還有一個 ID 非常重要,在 EagleEye 里面被稱作 RpcId。RpcId 用于區(qū)分同一個調(diào)用鏈下的多個網(wǎng)絡調(diào)用的發(fā)生順序和嵌套層次關系。
當這個前端執(zhí)行業(yè)務處理需要發(fā)起 RPC 調(diào)用時,RPC 調(diào)用客戶端會首先從當前線程 ThreadLocal 上面獲取之前 EagleEye 設置的調(diào)用上下文;然后,把 RpcId 遞增一個序號;之后,調(diào)用上下文會作為附件隨這次請求一起發(fā)送到下游的服務器。
關于鷹眼的詳細介紹,這里有一篇分享非常不錯,即 鷹眼下的淘寶:分布式調(diào)用跟蹤系統(tǒng)。
總結(jié)
這一課時主要分享了分布式調(diào)用跟蹤的應用場景、調(diào)用鏈的日志結(jié)構(gòu)、分布式鏈路跟蹤的選型實現(xiàn)等。
現(xiàn)在思考一下,了解了鏈路跟蹤的日志格式,如果讓你來設計一個調(diào)用跟蹤系統(tǒng),除了基本的鏈路跟蹤功能,還需要滿足哪些功能設計呢?
舉個例子,在實際業(yè)務中,鏈路跟蹤系統(tǒng)會有一個采樣率配置,不會監(jiān)控全部的鏈路,其實是考慮到對系統(tǒng)性能的影響。所以,作為非業(yè)務組件,應當盡可能少侵入或者無侵入其他業(yè)務系統(tǒng),并且盡量少的占用系統(tǒng)資源。