【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

螞蟻金服分散式架構發表於2018-08-22

SOFA

Scalable Open Financial Architecture

是螞蟻金服自主研發的金融級分散式中介軟體,包含了構建金融級雲原生架構所需的各個元件,是在金融場景裡錘鍊出來的最佳實踐。

本文為《剖析 | SOFARPC 框架》第二篇,本篇由暢為/碧遠/卓與共同出品。
《剖析 | SOFARPC 框架》系列由 SOFA 團隊和原始碼愛好者們出品,

專案代號:SOFARPCLab,文章尾部有參與方式,歡迎同樣對原始碼熱情的你加入


一. 前言

微服務已經被廣泛應用在工業界,微服務帶來易於團隊並行開發、獨立部署、模組化管理等諸多優點。然而微服務將原單體拆分多個模組獨立部署,各模組之間連結變得錯綜複雜,在大規模分散式系統中這種複雜鏈路給維護帶來了諸多困難。 如果對整個微服務架構不能瞭然於胸,便很難理清各模組之間的呼叫關係。 例如修改一個服務介面,對哪些服務造成影響不能快速定位。


SOFARPC 在 5.4.0 以後提供了鏈路追蹤技術,可以有效協助開發運營人員進行故障診斷、容量預估、效能瓶頸定位以及呼叫鏈路梳理。


本文將從以下兩個方面介紹目前已經開源的 SOFARPC 的鏈路追蹤技術:

  • 什麼是鏈路追蹤技術

  • SOFARPC 鏈路追蹤設計原理


二. 什麼是鏈路追蹤技術


鏈路追蹤技術主要是收集、儲存、分析分散式系統中的呼叫事件資料,協助開發運營人員進行故障診斷、容量預估、效能瓶頸定位以及呼叫鏈路梳理。 鏈路追蹤技術包含了資料埋點、收集、儲存、分析等相關技術,是一套技術體系。 大部分的鏈路追蹤框架都是參考 Google 鏈路追蹤系統 Dapper 的一篇設計論文(《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》),SOFARPC 的 SOFATracer 的設計靈感也是來自這篇著名論文。


以大規模分散式電商系統為例,使用者下單購買某款產品時後端需要呼叫各系統或子模組進行協作,共同完成一個使用者請求。 如下圖所示,使用者的下單行為涉及到了A、B、C、D、E、F 6 個系統的協同工作,這些系統都是以叢集形式部署。整個鏈路中最長的鏈路呼叫是3層,如 A-> C -> E 或 A -> C -> F。

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

模組的增多加大了系統出錯的概率,一旦因某系統/模組出錯導致整個請求呼叫出錯,在缺乏鏈路追蹤的條件下很難定位具體出錯的模組,只能通過日誌搜尋定位。 在實際生產環境下比較關注一個請求中的各個模組的耗時情況、連續呼叫情況、出錯的節點等資訊。


為了解決上述問題,Dapper 提供了一套解決方案。整個方案分為資料收集、儲存和分析幾個部分。分散式追蹤技術會記錄一個請求中各模組的呼叫資訊;並通過一個處理叢集把所有機器上的日誌增量地收集到叢集當中進行處理,將同一個請求的日誌串聯;最後視覺化顯示呼叫情況。


常用的資料收集方式為埋點,通過在公共元件如 RPC 等注入程式碼,收集服務呼叫的相關資訊。目前大部分鏈路呼叫系統如 Dapper、鷹眼、Spring Cloud Sleuth 都在用這種模式。同樣 SOFARPC 作為一個公共的通訊框架,在金融業務領域被廣泛應用,因此適合作為埋點,這樣無需業務端自行埋點,可做到對業務程式碼的無侵入性。


Dapper 將一個呼叫過程構建成一棵呼叫樹(稱為 Tracer ),Tracer 樹中的每個節點表示鏈路呼叫中的一個模組或系統。 通過一個全域性唯一的 traceId 來標識一個請求呼叫鏈。 並定義了span,span表示一個系統的呼叫,一個 span 包含以下階段:

  • Start: 發起呼叫

  • cleint send(cs) : 客戶端傳送請求

  • Server Recv(sr) :服務端收到請求

  • Server Send(ss) : 服務端傳送響應

  • Client Recv(cr) : 客戶端收到服務端響應

  • End: 整個鏈路完成

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

(圖來自 Dapper 論文)

每個 span 包含兩個重要的資訊 span id(當前模組的span id) 和 span parent ID (上一個呼叫模組的 span id ),通過這兩個資訊可以定位一個span 在呼叫鏈的位置。 通過以上資訊我們可以定義使用者下單過程的呼叫鏈:

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

SOFARPC 中的鏈路追蹤技術主要是作為埋點部分,因此對於鏈路追蹤系統的收集和分析部分本文不做詳述,想要深入瞭解的可參看參考資料內容。鏈路追蹤可以提供我們以下功能:

  1. 服務耗時、瓶頸分析 :分析每個服務的耗時情況,可以針對耗時長的服務進行優化,提高服務效能。

  2. 視覺化錯誤:快速定位服務鏈路中出錯的環境,便於排查和定位問題。一般鏈路追蹤中介軟體都提供了 ZipKin 頁面支援。

  3. 鏈路優化: 對於呼叫比較頻繁的服務,可以針對這些服務實施一些優化措施。

  4. 呼叫鏈路梳理:通過視覺化介面,對整個呼叫鏈路有個清晰的認識。

在設計分散式鏈路框架時需要考慮一下幾個問題:

  1. 低損耗、高效能: 追蹤系統對線上服務的影響應該做到足夠小,不影響線上服務效能。

  2. 應用透明: 對於業務開發人員來說,應不需要知道有跟蹤系統這回事的。

  3. 可擴充套件性:雖則業務規則增大、叢集增多,監控系統都應該能完全把控住這種快速變化。

  4. 資料取樣設計:如果每條日誌都記錄,在高併發情況下對系統有一定的損耗。但收集資料太少可能對統計結果有所影響,所以應合理設計取樣比例。

三. SOFARPC 鏈路追蹤設計原理

SOFARPC 作為一個基礎的通訊中介軟體,對服務呼叫有很強的感知能力,容易獲取鏈路追蹤所需的服務呼叫資訊。因此很多鏈路追蹤系統都會選擇RPC 作為埋點物件,通過對 RPC中介軟體的埋點可以輕鬆做到對使用者的無感知、透明化。 SOFARPC 在 5.4.0 以後開始支援分散式鏈路追蹤,其技術實現主要依賴於所整合的 SOFATracer。


SOFARPC 不僅提供了埋點資訊採集的能力, 還支援資料上報 zipkin。 通過 SOFARPC + SOFATracer + zipKin 可以快速搭建一套完整的鏈路追蹤系統,包括埋點、收集、分析展示等。 收集和分析主要是借用 zipKin 的能力,本文重點講 SOFARPC 中的資料埋點設計。SOFARPC 自身具備的微核心設計和可擴充性,使得在 SOFARPC 在不破壞開放封閉原則的前提下,比較輕鬆地集合 SOFATracer。


該部分主要從以下幾個方面討論 SOFARPC 的鏈路追蹤設計思路:

  1. 可插拔設計。 SOFARPC 採用了微核心設計,使得很容易擴充套件,增加新功能。

  2. 匯流排設計。為資料埋點做提供一種無侵入的擴充套件方式。

  3. 呼叫 trace 和 span

  4. 資料取樣設計

  5. 非同步重新整理機制

  6. 耗時計算:鏈路呼叫的耗時統計等資訊獲取。

  7. 埋點資料透傳,各模組之間的鏈路呼叫資料的透傳機制。

  8. 非同步執行緒的鏈路呼叫。在非同步多執行緒環境下如何保證 traceId 和 spanId 的有序性。

  9. 鏈路呼叫日誌資料的檔案儲存結構

3.1 可插拔設計

SOFARPC 自身具備的微核心設計和可擴充性,使得在 SOFARPC 在不破壞開放封閉原則的前提下,比較輕鬆地集合 SOFATracer。SOFARPC 採用了自己實現的一套 SPI 機制, 通過該 SPI 機制動態去載入其他模組、過濾器、協議等,實現靈活擴充。SOFARPC 為了整合 SOFATracer 也採用了這套機制,做到可插拔。

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

SofaTracerModule 類實現了 Module 介面,並增加 @Extension ("sofaTracer") 註解,方便SOFARPC 在啟動時將相關模組載入進來。 SofaTracerModule 作為 SOFA-PRC 鏈路追蹤的入口,在 SofaTracerModule 模組被載入時完成一些事件的訂閱。

這裡會訂閱 9 種事件, 通過監聽 SOFARPC 的這 9 種事件,來完成埋點資料的獲取和非同步磁碟寫入操作。SOFARPC 通過事件匯流排設計來訂閱這些事件,當事件發生時通知對應的訂閱者做相應的操作。

3.2 事件匯流排設計

事件匯流排(EventBus)設計也是 SOFARPC 的一個具有很強擴充套件性的設計,EventBus 類似計算機資料匯流排,用於傳輸資料,EventBus 主要是傳輸事件資料。 EventBus 採用了釋出-訂閱設計模式,在SOFARPC 服務呼叫的整個過程中設定多個事件點,當這些事件發生時就建立事件寫入到 EventBus,訂閱者可以訂閱匯流排中感興趣的事件並處理。如圖所示:

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

如上圖所示,SOFATracer 訂閱了在RPC呼叫週期的9種事件,當這些事件發生時會建立事件傳入到 EventBus。 EventBus 中一旦釋出新的事件就會通知所有感興趣的訂閱者,SAFA-Tracer 統一採用 SofaTracerSubscriber 訂閱和處理這 9 種事件,最終鏈路追蹤資料的獲取操作都交給了 RpcSofaTracer 處理。

這種匯流排設計使得 SOFARPC 在集合 SOFATracer 時無需為了獲取資料而破壞原來程式碼的封裝性,使用無侵入的方式來完成埋點和資料的獲取。

3.3 呼叫鏈 Trace 和 Span

SOFATracer 的設計思路也是來自 Dapper, 因此也提供了呼叫樹 Trace 和 Span。

  • Trace 是一次完整的跟蹤,從請求到伺服器開始,伺服器返回 response 結束,跟蹤每次rpc呼叫的耗時。 Trace 是一個類似樹狀呼叫鏈,樹中的每個節點對應一個系統或服務節點。

  • Span 是一個更小的單位,表示一個 RPC 呼叫過程。在 SOFARPC 中分為 ClientSpan 和 ServerSpan。 ClientSpan 記錄從客戶端傳送請求給服務端,到接受到服務端響應結果的過程。ServerSpan 是服務端收到客戶端時間 到 傳送響應結果給客戶端的這段過程。

一個 span 包含幾種 Annotation:

  1. cs (client send)

  2. cr (client recv)

  3. sr (server recv)

  4. ss (server send)

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

如上圖所示展示了兩個系統呼叫中的 client span 和 server span 的關係, 一次 RPC 呼叫稱為 span, 併產生一個 spanId, client span 的 spanId 和 server span的 spanId 是同一個,因為都在一個 RPC 呼叫中。 下圖展示從時間的維度來解釋這兩者的關係:

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

3.3.1 TraceId 生成規則

TraceId 指的是 SOFATracer 中代表唯一一次請求的 ID,此 ID 一般由叢集中第一個處理請求的系統產生,並在分散式呼叫下通過網路傳遞到下一個被請求系統。

traceId 應當保證全域性唯一,如何做到全域性唯一呢?TraceId 目前的生成的規則參考了淘寶的鷹眼元件:

伺服器 IP + 產生 ID 時候的時間 + 自增序列 + 當前程式號 
複製程式碼

如下圖所示:

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

  • 自增的序列,從 1000 漲到 9000,到達 9000 後回到 1000 再開始往上漲。

根據這種方式計算出的 traceId 為 0ad1348f1403169275002100356696,可以保證 traceId 全域性唯一。

3.3.2 SpanId 生成規則

SpanId 表示整個鏈路中一次rpc呼叫的序號,也代表了本次請求在整個呼叫鏈路中的位置或者說層次,比如 A 系統在處理一個請求的過程中依次呼叫了 B,C,D 三個系統,那麼這三次呼叫的的 SpanId 分別是:0.1,0.2,0.3。如果 C 系統繼續呼叫了 E,F 兩個系統,那麼這兩次呼叫的 SpanId 分別是:0.2.1,0.2.2。

SOFARPC 和 Dapper不同,spanId中已經包含了呼叫鏈上下文關係,包含 parent spanId 的資訊。如圖所示:

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

3.4 資料取樣設計

在 Dapper 論文中強調了資料取樣的重要性,如果將每條埋點資料都重新整理到磁碟上會增大鏈路追蹤框架對原有業務效能的影響。如果取樣率太低,可能會導致一些重要資料的丟失。 論文中提到如果在高併發情況下 1/1024 的取樣率是足夠的,也不必擔心重要事件資料的丟失。因為在高併發環境下,一個異常資料出現一次,那麼就會出現 1000 次。 然而在併發量不是很多的系統,並且對資料各位敏感時需要讓業務開發人員手動設定取樣率。

SOFATracer 的不支援配置 取樣率,但取樣率也不是一個固定寫死的值,而是採用自適應取樣率。 在 SOFATracer 中提供了 RingBuffer 的資料結構,設定一個 1024 的序列化槽位用於儲存每個鏈路呼叫的埋點資料。 可以看做是一個圓形環狀結構,RingBuffer 中的資料 從槽位 0 開始儲存,一直儲存到1023。 當操作1023 時會從頭開始存放,放在原來槽位的資料將被覆蓋。

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

從上圖所示,當資料寫入的速度遠遠大於 3個 consumer 的處理速度,那麼環上的資料在未被處理時就被覆蓋。 通過覆蓋的方式來自動調整取樣率,併發性越高、寫入速度越快時,取樣率就越低。當併發性越低、寫入速度也隨之變慢,則取樣率就變高。

在 SOFATracer 中預設開啟三個執行緒去負責這些資料的持久化。假設每個執行緒的處理速度是 x/s(條/秒), 併發寫入的速度是 y/s,那麼 SOFATracer 的自動化取樣率為 3x / y (前提是 y >= 3x, 否則就是 100% 取樣率)。

3.5 非同步重新整理機制

埋點資料的本地化儲存涉及到磁碟操作, 磁碟 IO 速度較慢,如果在高併發環境下同步重新整理磁碟給原業務帶來的效能損耗是非常可觀的。 鏈路追蹤系統在資料埋點的時候應儘可能的降低系統損耗,對原業務在邏輯和效能上做到無侵入性。

SOFATracer 採用了非同步重新整理機制,將 RingBuffer 的資料非同步重新整理到磁碟。 預設情況下 會啟動 3個處理執行緒去處理 RingBuffer 的資料,將資料非同步重新整理到磁碟進行持久化。

當 RingBuffer 寫入新資料時就會喚醒處理執行緒, 並將當前存入資料的槽位設定為可用槽位。 處理執行緒從睡眠點醒來後,便從原來處理位置往下獲取資料並處理,直到所處理資料槽位大於可用槽位,則阻塞等待。

3.6 耗時計算

耗時計算不是為了整合 SOFATracer 而單獨設定的,SOFARPC 框架自身帶有耗時計算的邏輯,這些時間可以用於判斷 RPC 呼叫是否超時等。因此加入鏈路追蹤埋點時,不需要在擴充套件模組中計算耗時時間,SOFARPC中已經將呼叫的時間耗時等資訊放在 RpcInternalContext 上下文中。 在計算 RPC 呼叫耗時時,對原有框架效能不影響,直接去上下文獲取即可。

3.7 埋點資料透傳

各模組部署後獨立收集埋點日誌,這些呼叫鏈日誌通過 traceId 串聯在一起。 在 SOFARPC中,下一個模組的 spanId 的建立依賴於上一個模組的 spanId。 因此這些埋點資料如 traceId 以及 spanId 需要透傳給下游模組。

資料傳輸一般有兩種:

1. 帶內透傳,即在原來的 rpc 呼叫請求網路寬頻中加入埋點資料透傳給下游;

2. 帶外傳播,通過單獨提供一個寬頻來傳播,不影響原呼叫資料和網路。


Dapper 採用帶外傳播,這種方式可以不影響原有業務效能。帶內透傳資料意味著需要增加原來網路呼叫的負載。SOFARPC 採用的是帶內透傳,直接在原來的 RPCRequest 的擴充套件欄位中加入埋點資料,直接透傳給下游。SOFARPC 的 spanId 長度相對較短,所需傳遞的資料相對較小,從整體上看對原業務效能影響較小。

3.8 非同步執行緒的鏈路調動

在多執行緒併發呼叫環境下的資料鏈路埋點也是一個值得關注問題,當一個服務考慮效能問題可能會起多個執行緒同時呼叫其他不同的模組。鏈路系統如何保證這些呼叫還是符合 openTrace 規範,保證 traceId 和 spanId 有序。

【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

一個鏈路呼叫在模組 A 是一個執行緒,鏈路呼叫的上下文資訊如 traceId、spanId 等都是存放在 ThreadLocal。 按上圖思路新起的執行緒 1、2、3 無法獲取主執行緒的 ThreadLocal 資料,即無法獲取呼叫鏈路資料。那麼在無法獲取鏈路呼叫的上線文資料時進行模組 B、C、D 的呼叫操作會導致收集得到的埋點資料是亂序的髒資料。

為了避免啟動新執行緒把 鏈路呼叫的上下文 資訊丟失,SOFATracer 提供了SofaTracerCallable 類,只要使用該類來實現執行緒邏輯,SOFATracer 會自動將鏈路呼叫的上下文資訊透傳給 SofaTracerCallable,因此可以像單執行緒一樣進行呼叫埋點。SOFATracer 將上下文中的一些欄位設定為執行緒安全,同樣保證了多執行緒環境下的資料安全問題。 因此建議在多執行緒環境下進行一步呼叫時儘可能考慮使用 SofaTracerCallable, 否則呼叫鏈資料與預期有些出路。

3.9 檔案儲存結構

SOFA 整體開源框架對日誌做了很好地分類,將不同型別的日誌存放在不同的資料夾下。一方面便於收集特定日誌,如埋點資料;另一方面也便於查詢問題方便,日誌結構和內容清晰。

在 SOFARPC 的鏈路追蹤技術中,埋點資料的儲存也採用日誌檔案方式進行持久化儲存。tracer 日誌檔案包含以下檔案:


檔案

功能

rpc-client-digest.log

記錄client rpc 呼叫的鏈路呼叫資料

rpc-client-stat.log

記錄 client rpc 鏈路呼叫的統計資料

rpc-server-digest.log

記錄 server rpc 呼叫的鏈路呼叫資料

rpc-server-stat.log

記錄 server rpc 鏈路呼叫的統計資料

static-info.log

統計資訊日誌

tracer-self.log

tracer 自身的日誌記錄

四. 總結

SOFARPC 的依靠整合 SOFATrace 來實現鏈路追蹤技術,SOFARPC 作為公共元件在整個鏈路追蹤技術系統中負責資料埋點工作。依賴 SOFARPC 自身強大的可擴充套件性設計,如微核心設計和事件匯流排設計,使得 SOFARPC 在不破壞開發封閉原則的基礎上快速實現了鏈路追蹤埋點工作。 SOFARPC 的鏈路追蹤技術具有以下特點:

    1. 作為公共基礎的通訊元件,SOFARPC 的鏈路追蹤埋點對業務程式碼實現零侵入。

    2. 採用日誌資料非同步重新整理機制,不影響正常業務效能。

    3. 採用了自適應取樣設計,巧妙平衡了資料採集和效能的問題。

    4. 支援資料上報 zipkin, 通過與 zipkin 結合可以快速構建一個完整的連續追蹤系統。

    5. 解決了非同步執行緒鏈路呼叫資料問題。

    6. 採用了 OpenTracing 規範,因此可以和其他鏈路追蹤手機和展示的技術框架快速整合。


    五. 參考資料

    • [1] 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》

    • [2] 全鏈路穩定性背後的數字化支撐:阿里巴巴鷹眼技術解密


    歡迎加入參與 SOFARPC 原始碼解析

    【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析


    SOFARPC 原始碼解析目錄:

    我們會逐步詳細介紹每部分的程式碼設計和實現,預計會按照如下的目錄進行,以下也包含目前的原始碼分析文章的認領情況:

    • 【已完成】SOFARPC 框架之總體設計與擴充套件機制

    • 【已完成】SOFARPC 鏈路追蹤剖析

    • 【已完成】SOFARPC 同步非同步實現剖析

    • 【已認領】SOFARPC 執行緒模型剖析

    • 【已認領】SOFARPC 連線管理與心跳剖析

    • 【已認領】SOFARPC 單機故障剔除剖析

    • 【待認領】SOFARPC 路由實現剖析

    • 【待認領】SOFARPC 序列化比較

    • 【待認領】SOFARPC 註解支援剖析

    • 【待認領】SOFARPC 優雅關閉剖析

    • 【待認領】SOFARPC 資料透傳剖析

    • 【待認領】SOFARPC 跨語言支援剖析

    • 【待認領】SOFARPC 泛化呼叫實現剖析


    領取方式:

    直接回複本公眾號想認領的文章名稱,我們將會主動聯絡你,確認資質後,即可加入<SOFA:RPCLab/>,It's your show time!


    除了原始碼解析,也歡迎提交 issue 和 PR:

    SOFA: https://github.com/alipay

    SOFARPC:https://github.com/alipay/sofa-rpc

    SOFABolt:https://github.com/alipay/sofa-bolt

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


    【剖析 | SOFARPC 框架】系列之鏈路追蹤剖析

    長按關注,獲取分散式架構乾貨

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



    相關文章