歡迎光臨
每天分享高質量文章

螞蟻金服分佈式鏈路跟蹤組件埋點機制 | 剖析

SOFA

Scalable Open Financial Architecture 是螞蟻金服自主研發的金融級分佈式中間件,包含了構建金融級雲原生架構所需的各個組件,是在金融場景里錘煉出來的最佳實踐。

SOFATracer 是一個用於分佈式系統呼叫跟蹤的組件,通過統一的 TraceId 將呼叫鏈路中的各種網絡呼叫情況以日誌的方式記錄下來,以達到透視化網絡呼叫的目的,這些鏈路資料可用於故障的快速發現,服務治理等。

本文為《剖析 | SOFATracer 框架》最後一篇,本篇作者sqyu,來自小象生鮮。《剖析 | SOFATracer 框架》系列由 SOFA 團隊和原始碼愛好者們出品,專案代號: ,目前已經全部完成,可在文末獲取本系列文章目錄,感謝大家的參與。

SOFATracer

https://github.com/alipay/sofa-tracer

前言

自 Google《Dapper,大規模分佈式系統的跟蹤系統》論文發表以來,開源 Tracer 系統如雨後春筍般相繼面市,各顯神通,但都是用於分佈式系統呼叫跟蹤的組件,通過統一的 traceId 將呼叫鏈路中的各種網絡呼叫情況記錄下來,以達到透視化網絡呼叫的目的。本文介紹的 SOFATracer 是以日誌的形式來記錄的,這些日誌可用於故障的快速定位,服務治理等。目前來看 SOFATracer 團隊已經為我們搭建了一個完整的 Tracer 框架內核,包括資料模型、編碼器、跨行程透傳 traceId、採樣、日誌落盤與上報等核心機制,並提供了擴展 API 及基於開源組件實現的部分插件,為我們基於該框架打造自己的 Tracer 平臺提供了極大便利。

作為一個開源實現,SOFATracer 也盡可能提供大而全的插件實現,但由於多數公司都有自己配套的技術體系,完全依賴官方提供的插件可能無法滿足自身的需要,因此如何基於 SOFATracer 自身 API 的組件埋點機制進行擴展,實現自己的插件是必須掌握的一項本領。

本文將根據 SOFATracer 自身 API 的擴展點及已提供的插件原始碼來分析下 SOFATracer 插件的埋點機制。

SOFATracer 的插件埋點機制

對一個應用的跟蹤要關註的無非就是 客戶端->web 層->rpc 服務->dao 後端儲存、cache 快取、訊息佇列 mq 等這些基礎組件。SOFATracer 插件的作用實際上也就是對不同組件進行埋點,以便基於這些組件採集應用的鏈路資料。

不同組件有不同的應用場景和擴展點,因此對插件的實現也要因地制宜,SOFATracer 埋點方式一般是通過 Filter、Interceptor 機制實現的。

組件擴展入口之 Filter or Interceptor

SOFATracer 目前已實現的插件中,像 SpringMVC 插件是基於 Filter 進行埋點的,httpclient、resttemplate 等是基於 Interceptor 機制進行埋點的。在實現插件時,要根據不同插件的特性和擴展點來選擇具體的埋點方式。正所謂條條大路通羅馬,不管怎麼實現埋點,都是依賴 SOFATracer 自身 API 的擴展機制來實現。

API 擴展點之 AbstractTracer API

SOFATracer 中所有的插件均需要實現自己的 Tracer 實體,如 SpringMVC 的 SpringMvcTracer 、HttpClient 的 HttpClientTracer 等。

  • 基於 SOFATracer API 埋點方式插件擴展如下:

AbstractTracer 是 SOFATracer 用於插件擴展使用的一個抽象類,根據插件型別不同,又可以分為 clientTracer 和 serverTracer,分別對應於 AbstractClientTracer 和 AbstractServerTracer;再通過 AbstractClientTracer 和 AbstractServerTracer 衍生出具體的組件 Tracer 實現,比如上圖中提到的 HttpClientTracer 、RestTemplateTracer 、SpringMvcTracer 等插件 Tracer 實現。

AbstractTracer

這裡先來看下 AbstractTracer 這個抽象類中具體提供了哪些抽象方法,也就是對於 AbstractClientTracer 和 AbstractServerTracer 需要分別擴展哪些能力。

從上圖  AbstractTracer 類提供的抽象方法來看,不管是 client 還是 server,在具體的 Tracer 插件實現中,都必須提供以下實現:

  • DigestReporterLogName :當前組件摘要日誌的日誌名稱

  • DigestReporterRollingKey : 當前組件摘要日誌的滾動策略

  • SpanEncoder:對摘要日誌進行編碼的編碼器實現

  • AbstractSofaTracerStatisticReporter : 統計日誌 reporter 類的實現類

基於 SOFATracer 自身 API 埋點最大的優勢在於可以通過上面的這些引數來實現不同組件日誌之間的隔離,上述需要實現的這些點是實現一個組件埋點常規的擴展點,是不可缺少的。

上面分析了 SOFATracer API 的埋點機制,並且對於一些需要擴展的核心點進行了說明。SOFATracer 自身提供的內核非常簡單,其基於自身 API 的埋點擴展機製為外部用戶定製組件埋點提供了極大的便利。下麵以 Thrift 擴展,具體分析如何實現一個組件埋點。

PS : Thrift 是外部用戶基於 SOFATracer API 擴展實現的,目前僅用於其公司內部使用,SOFATracer 官方組件中暫不支持,請知悉;後續會溝通作者提供 PR ,在此先表示感謝。

Thrift 插件埋點分析

這裡我們以 Thrift RPC 插件實現為例,分析如何實現一個埋點插件。

  • 1、實體工程的分包結構

從上圖插件的工程的包結構可以看出,整個插件實現比較簡單,代碼量不多,但從類的定義來看,直觀的體現了SOFATracer 插件埋點機制所介紹的套路。下麵將進行詳細的分析與介紹。

  • 2、實現 Tracer 實體

RpcThriftTracer 繼承了 AbstractTracer 類,是對 clientTracer、serverTracer 的擴展。

PS:如何確定一個組件是 client 端還是 server 端呢?就是看當前組件是請求的發起方還是請求的接受方,如果是請求發起方則一般是 client 端,如果是請求接收方則是 server 端。那麼對於 RPC 來說,即是請求的發起方也是請求的接受方,因此這裡實現了 AbstractTracer 類。

  • 3、擴展點類實現

PS:上面表格中 SpanEncoder 和 AbstractSofaTracerStatisticReporter 的實現中,多了一層AbstractRpcDigestSpanJsonEncoder 和AbstractRpcStatJsonReporter的抽象,主要是由於 client 和 server 端有公共的邏輯處理,為了減少冗餘代碼,而採用了多繼承樣式處理。

  • 4、資料傳播格式實現

SOFATracer 支持使用 OpenTracing 的內建格式進行背景關係傳播。

  • 5、Thrift Rpc 自身擴展點之請求攔截埋點

我們內部 Thrift 支持 SPI Filter 機制,因此要實現對請求的攔截過濾,示例插件埋點的實現就是基於 SPI Filter 機制完成的。其中 FilterThriftBase 抽象也是為了便於處理 consumerFilter 和 providerFilter 公共的邏輯抽象。

插件擴展基本思路總結

對於一個組件來說,一次處理過程一般是產生一個 Span;這個 Span 的生命周期是從接收到請求到傳迴響應這段過程。
但是這裡需要考慮的問題是如何與上下游鏈路關聯起來呢?在 Opentracing 規範中,可以在 Tracer 中 extract 出一個跨行程傳遞的 SpanContext 。然後通過這個 SpanContext 所攜帶的信息將當前節點關聯到整個 Tracer 鏈路中去,當然有提取(extract)就會有對應的註入(inject);更多請參考 
螞蟻金服分佈式鏈路跟蹤組件鏈路透傳原理與SLF4J MDC的擴展能力分析 | 剖析 。

鏈路的構建一般是 client-server-client-server 這種樣式的,那這裡就很清楚了,就是會在 client 端進行註入(inject),然後再 server 端進行提取(extract),反覆進行,然後一直傳遞下去。
在拿到 SpanContext 之後,此時當前的 Span 就可以關聯到這條鏈路中了,那麼剩餘的事情就是收集當前組件的一些資料;整個過程大概分為以下幾個階段:

  • 從請求中提取 spanContext

  • 構建 Span,並將當前 Span 存入當前 tracer背景關係中(SofaTraceContext.push(Span))

  • 設置一些信息到 Span 中

  • 傳迴響應

  • Span 結束&上報

下麵結合 SOFATracer 自身 API 原始碼來逐一分析下這幾個過程。

從請求中提取 spanContext

Thrift 插件中的 Consumer 和 Provider 分別對應於 client 和 server 端存在的,所以在 client 端就是將當前請求執行緒的產生的 traceId 相關信息 Inject 到 SpanContext,server 端從請求中 extract 出 spanContext,來還原本次請求執行緒的背景關係。
相關處理邏輯在FilterThriftBase抽象類中,如下圖:

  • inject 實現代碼

  • extract 實現代碼

獲取 Span & 資料獲取

serverReceive 這個方法是在 AbstractTracer 類中提供了實現,子類不需要關註這個。在 SOFATracer 中也是將請求大致分為以下幾個過程:

  • 客戶端發送請求 clientSend cs

  • 服務端接受請求 serverReceive sr

  • 服務端傳回結果 serverSend ss

  • 客戶端接受結果 clientReceive cr

無論是哪個插件,在請求處理周期內都可以從上述幾個階段中找到對應的處理方法。因此,SOFATracer 對這幾個階段處理進行了封裝。見下圖:
     

       
這四個階段實際上會產生兩個 Span,第一個 Span 的起點是 cs,到 cr 結束;第二個 Span 是從 sr 開始,到 ss 結束。

  1. clientSend    serverReceive    …    serverSend clientReceive  

    
    

來看下 Thrift Rpc 插件中 Consumer 和 Provider 的實現

  • ConsumerTracerFilter

紅色框內對應的客戶端發送請求,也就是 cs 階段,會產生一個 Span。

  • ProviderTracerFilter

服務端接收請求 sr 階段,產生了一個 Span 。上面appendProviderRequestSpanTags這段代碼是為當前這個 Span 設置一些基本信息,包括當前應用的應用名、當前請求的 service、當前請求的請求方法以及請求大小等。

傳迴響應與結束 Span

在 Filter 鏈執行結束之後,ConsumerTracerFilter(見圖一)和 ProviderTracerFilter(見圖二) 分別在 finally 塊中又補充了當前請求響應結果的一些信息到 Span 中去。然後分別呼叫 clientReceive 和 serverSend 結束當前 Span。

  • 圖一

  • 圖二

關於 clientReceive 和 serverSend 裡面呼叫 Span.finish 這個方法( opentracing 規範中,Span.finish 的執行標志著一個 Span 的結束(見圖一),當呼叫finish執行邏輯時同時會進行span資料的上報(見圖二)和當前請求執行緒MDC資源的清理操作(見圖三)等。

  • 圖一:

當前 Span 資料上報,代碼如下:

  • 圖二:

清理當前請求執行緒的 MDC 資源的一些邏輯處理等,代碼如下:

  • 圖三:

插件編寫流程總結

上述以自定義 Thrift RPC 插件為例,分析了下 SOFATracer 插件埋點實現的一些細節。前面不僅總結了編寫插件的基本埋點思路而且還對 SOFATracer 自身 API 實現做了相應的分析。基於此本節則從整體思路上來總結如何編寫一個 SOFATracer 的插件:

  • 1、確定所要實現的插件,理解該組件的使用場景和擴展點,然後確定以哪種方式來埋點,比如:是 Filter or Interceptor

  • 2、實現當前插件的 Tracer 實體,這裡需明確當前插件是以 client 存在還是以 server 存在

  • 3、實現一個列舉類,用來描述當前組件的日誌名稱和滾動策略 key 值等

  • 4、實現插件摘要日誌的 Encoder ,實現當前組件的定製化輸出

  • 5、實現插件的統計日誌 Reporter 實現類,通過繼承 AbstractSofaTracerStatisticReporter 類並重寫 doReportStat

  • 6、定義當前插件的傳播格式

  • 7、要明確我們需要收集哪些資料

小結

本文通過對 SOFATracer 插件的埋點機制進行分析介紹,並結合自定義 Thrift RPC 插件的埋點實現進行了分析。希望通過本文能夠讓更多的同學理解基於 SOFATracer 自身 API 的埋點實現,能根據自身需要實現自己的插件。

文中涉及到的鏈接:

  • 《Dapper,大規模分佈式系統的跟蹤系統》:

    https://bigbully.github.io/Dapper-translation/

 

【螞蟻金服分佈式鏈路跟蹤組件 SOFATracer | 剖析】系列

長按關註,獲取分佈式架構乾貨

歡迎大家共同打造 SOFAStack https://github.com/alipay

    赞(0)

    分享創造快樂