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