本文介紹在使用可觀測鏈路 OpenTelemetry 版之前需要了解的基本概念,包括分布式追蹤系統的作用,什么是調用鏈,可觀測鏈路 OpenTelemetry 版所依賴的OpenTracing數據模型,以及在可觀測鏈路 OpenTelemetry 版產品里數據是如何上報的。
為什么需要分布式追蹤系統?
為了應對各種復雜的業務,開發工程師開始采用敏捷開發、持續集成等開發方式。系統架構也從單機大型軟件演化成微服務架構。微服務構建在不同的軟件集上,這些軟件模塊可能是由不同團隊開發的,可能使用不同的編程語言來實現,還可能發布在多臺服務器上。因此,如果一個服務出現問題,可能導致幾十個應用都出現服務異常。
分布式追蹤系統可以記錄請求范圍內的信息,例如一次遠程方法調用的執行過程和耗時,是我們排查系統問題和系統性能的重要工具。
什么是調用鏈(Trace)?
在廣義上,一個調用鏈代表一個事務或者流程在(分布式)系統中的執行過程。在OpenTracing標準中,調用鏈是多個Span組成的一個有向無環圖(Directed Acyclic Graph,簡稱DAG),每一個Span代表調用鏈中被命名并計時的連續性執行片段。
下圖是一個分布式調用的例子:客戶端發起請求,請求首先到達負載均衡器,接著經過認證服務、計費服務,然后請求資源,最后返回結果。
數據被采集存儲后,分布式追蹤系統一般會選擇使用包含時間軸的時序圖來呈現這個調用鏈。
OpenTracing數據模型
整體概念
OpenTracing中的調用鏈(Trace)通過歸屬于此調用鏈的Span來隱性地定義。一條調用鏈可以視為一個由多個Span組成的有向無環圖(DAG圖)。Span之間的關系被命名為References。例如下面的示例調用鏈就是由8個Span組成的。
單個Trace中Span間的因果關系
[Span A] ←←←(The root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C是Span A的子節點,ChildOf)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G在Span F后被調用, FollowsFrom)
有些情況下,使用下面這種基于時間軸的時序圖可以更好地展現調用鏈。
單個Trace中Span間的時間關系
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
鏈路
Tracer接口用于創建Span(startSpan函數)、解析上下文(Extract函數)和透傳上下文(Inject函數)。它具有以下能力:
創建一個新Span或者設置Span屬性
/** 創建和開始一個span,返回一個span,span中包括操作名稱和設置的選項。 ** 例如: ** 創建一個無parentSpan的Span: ** sp := tracer.StartSpan("GetFeed") ** 創建一個有parentSpan的Span ** sp := tracer.StartSpan("GetFeed",opentracing.ChildOf(parentSpan.Context())) **/ StartSpan(operationName string, opts ...StartSpanOption) Span
每個Span包含以下對象:
Operation name:操作名稱 (也可以稱作Span name)。
Start timestamp:起始時間。
Finish timestamp:結束時間。
Span tag:一組鍵值對構成的Span標簽集合。鍵值對中,鍵必須為String,值可以是字符串、布爾或者數字類型。
Span log:一組Span的日志集合。每次Log操作包含一個鍵值對和一個時間戳。鍵值對中,鍵必須為String,值可以是任意類型。
SpanContext: Span上下文對象。每個SpanContext包含以下狀態:
要實現任何一個OpenTracing,都需要依賴一個獨特的Span去跨進程邊界傳輸當前調用鏈的狀態(例如:Trace和Span的ID)。
Baggage Items是Trace的隨行數據,是一個鍵值對集合,存在于Trace中,也需要跨進程邊界傳輸。
References(Span間關系):相關的零個或者多個Span(Span間通過SpanContext建立這種關系)。
透傳數據
透傳數據分為兩步:
從請求中解析出SpanContext。
// Inject() takes the `sm` SpanContext instance and injects it for // propagation within `carrier`. The actual type of `carrier` depends on // the value of `format`. /** 根據format參數從請求(Carrier)中解析出SpanContext(包括traceId、spanId、baggage)。 ** 例如: ** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) ** clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier) **/ Extract(format interface{}, carrier interface{}) (SpanContext, error)
將SpanContext注入到請求中。
/** ** 將SpanContext中的traceId,spanId,Baggage等根據format參數注入到請求中(Carrier), ** e.g ** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header) ** err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, carrier) **/ Inject(sm SpanContext, format interface{}, carrier interface{}) error
數據是如何上報的?
不通過Agent而直接上報數據的原理如下圖所示。
通過Agent上報數據的原理如下圖所示。