上一章中有對(duì)分布式追蹤鏈路方案 (Jaeger, Zipkin, SkyWalking 等) 進(jìn)行了介紹,不同的方案適用不同場(chǎng)景记劝、團(tuán)隊(duì)慢叨、開(kāi)發(fā)語(yǔ)言。在接入分布式鏈路追蹤過(guò)程中丽惭,會(huì)有不同的團(tuán)隊(duì)使用不同的方案實(shí)現(xiàn)击奶,如 A 團(tuán)隊(duì)使用的是 Zipkin, 而 B 團(tuán)隊(duì)使用的 Jaeger, 這樣就會(huì)存在不同追蹤系統(tǒng)的 API 兼容問(wèn)題 (切換方案會(huì)帶來(lái)比較大的改動(dòng)),為了解決這個(gè)問(wèn)題责掏,誕生了 OpenTracing 規(guī)范柜砾。
OpenTracing 規(guī)范
OpenTracing 是?個(gè)輕量級(jí)的標(biāo)準(zhǔn)化層,它位于應(yīng)?程序/類庫(kù)和追蹤或?志分析程序之間换衬,是?套分布式追蹤協(xié)議痰驱,與平臺(tái)证芭,語(yǔ)??關(guān),統(tǒng)?接?担映,?便開(kāi)發(fā)接?不同的分布式追蹤系統(tǒng)废士。
語(yǔ)義規(guī)范 : 描述定義的數(shù)據(jù)模型 Tracer,Sapn 和 SpanContext 等蝇完;
語(yǔ)義慣例 : 羅列出 tag 和 logging 操作時(shí)官硝,標(biāo)準(zhǔn)的key值;
OpenTracing API
Trace
OpenTracing 中的 Trace(調(diào)?鏈)通過(guò)歸屬此鏈的 Span 來(lái)隱性定義短蜕。?條 Trace 可以認(rèn)為?個(gè)有多個(gè) Span 組成的有向?環(huán)圖(DAG圖)氢架,Span 是?個(gè)邏輯執(zhí)?單元,Span 與 Span 的因果關(guān)系命名為 References朋魔。
OpenTracing 定義兩種關(guān)系:
- Childof:如下例?中岖研, SpanC 是 childof SpanA
- FollowsFrom:如下例?中,SpanG 是 followsFrom SpanF
SpanC 是 childof SpanA, SpanG 是 FollowsFrom SpanF
childof指的是垂直鏈路, FollowsFrom 指的是橫向鏈路
Span
Span封裝了如下?tīng)顟B(tài)
操作名稱
開(kāi)始時(shí)間戳
結(jié)束時(shí)間戳
?組零或多個(gè)鍵:值結(jié)構(gòu)的 Span標(biāo)簽 (Tags)警检。鍵必須是字符串缎玫。值可以是字符串,布爾或數(shù)值類型.
?組零或多個(gè) Span?志 (Logs)解滓,其中每個(gè)都是?個(gè)鍵:值映射并與?個(gè)時(shí)間戳配對(duì)。鍵必須是字符串筝家,值可以是任何類型洼裤。 并?所有的 OpenTracing 實(shí)現(xiàn)都必須?持每種值
類型。?個(gè) SpanContext
零或多個(gè)因果相關(guān)的 Span 間的 References (通過(guò)那些相關(guān)的 Span 的 SpanContext )
SpanContext 封裝了如下?tīng)顟B(tài)
任何需要跟跨進(jìn)程 Span 關(guān)聯(lián)的溪王,依賴于 OpenTracing 實(shí)現(xiàn)的狀態(tài)(例如 Trace 和 Span 的 id)
鍵:值 結(jié)構(gòu)的跨進(jìn)程的 Baggage Items(區(qū)別于 span tag腮鞍,baggage 是全局范圍,在 span 間保持傳遞莹菱,?tag 是 span 內(nèi)部移国,不會(huì)被? span 繼承使?。)
Trace/Span Identity
Trace/Span Identity
Key
uber-trace-id
? Case-insensitive in HTTP
? Lower-case in protocols that preserve header case
Value
{trace-id}:{span-id}:{parent-span-id}:{flags}
? {trace-id} ? 64-bit or 128-bit random number in base16 format
? Can be variable length, shorter values are 0-padded on the left
? Clients in some languages support 128-bit, migration pending
? Value of 0 is not valid
? {span-id} ? 64-bit random number in base16 format
? Value of 0 is not valid
? {parent-span-id} ? 64-bit value in base16 format representing parent span id
? Deprecated, most Jaeger clients ignore on the receiving side, but still include it on the sending side
? 0 value is valid and means “root span” (when not ignored)
? {flags} ? One byte bitmap, as two hex digits
? Bit 1 (right-most, least significant, bit mask 0x01) is “sampled” flag
? 1 means the trace is sampled and all downstream services are advised to respect that
? 0 means the trace is not sampled and all downstream services are advised to respect that
? We’re considering a new feature that allows downstream services to upsample if they find their tracing level is too low
? Bit 2 (bit mask 0x02 ) is “debug” flag
? Debug flag should only be set when the sampled flag is set
? Instructs the backend to try really hard not to drop this trace
? Bit 3 (bit mask 0x04 ) is not used
? Bit 4 (bit mask 0x08 ) is “firehose” flag
? Spans tagged as “firehose” are excluded from being indexed in the storage
? The traces can only be retrieved by trace ID (usually available from other sources, like logs)
? Other bits are unused
Inject 和 Extract 操作
跨進(jìn)程道伟,機(jī)器通訊迹缀,通過(guò)傳遞 Spancontext 來(lái)提供?夠的信息建? span 間的關(guān)系。
SpanContext 通過(guò) Inject 操作向 Carrier 中增加蜜徽,傳遞后通過(guò) Extracted 從 Carrier 中取出祝懂。
Inject
// TracerWrapper tracer wrapper
func AddTracer(ctx context.Context, r *http.Request, tracer opentracing.Tracer, tags map[string]string) context.Context {
//初始化 tracer
opentracing.InitGlobalTracer(tracer)
var sp opentracing.Span
//從 header 中獲取 span
spanCtx, _ := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header))
if spanCtx != nil {
sp = opentracing.GlobalTracer().StartSpan(r.URL.Path, opentracing.ChildOf(spanCtx))
}else{
//如果 header 中沒(méi)有攜帶 context, 則新建 span
sp = tracer.StartSpan(r.URL.Path)
}
//寫入 tag 或者 日志
for k, v := range tags {
// sp.LogKV(k, v)
// sp.SetTag(k, v)
sp.LogFields(
spanLog.String(k, v),
)
}
//注入span (用于傳遞)
if err := opentracing.GlobalTracer().Inject(
sp.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header)); err != nil {
logger.Fatalln("inject failed", err)
}
...
}
Extracted
// TracerWrapper tracer wrapper
func AddTracer(ctx context.Context, r *http.Request, tracer opentracing.Tracer, tags map[string]string) context.Context {
//初始化 tracer
opentracing.InitGlobalTracer(tracer)
var sp opentracing.Span
//從 header 中獲取 span
spanCtx, _ := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header))
if spanCtx != nil {
sp = opentracing.GlobalTracer().StartSpan(r.URL.Path, opentracing.ChildOf(spanCtx))
}else{
//如果 header 中沒(méi)有攜帶 context, 則新建 span
sp = tracer.StartSpan(r.URL.Path)
}
//寫入 tag 或者 日志
for k, v := range tags {
// sp.LogKV(k, v)
// sp.SetTag(k, v)
sp.LogFields(
spanLog.String(k, v),
)
}
...
}
- 分布式跟蹤(包擴(kuò)跨服務(wù),進(jìn)程拘鞋,主機(jī)等)需要對(duì) trace 上下? (spanContext) 進(jìn)?傳遞砚蓬,通過(guò) Inject ?法, 在 io.Writer 中注?上下?信息,服務(wù)端通過(guò) Extracted 取出盆色,通過(guò)對(duì)上下?和業(yè)務(wù)邏輯判斷 span 之間的關(guān)系(childof or followsfrom)灰蛙。
Sampling,采樣
OpenTracing API 不強(qiáng)調(diào)采樣的概念祟剔,但是?多數(shù)追蹤系統(tǒng)通過(guò)不同?式實(shí)現(xiàn)采樣。有些情況下摩梧,應(yīng)?系統(tǒng)需要通知追蹤程序物延,這條特定的調(diào)?需要被記錄,即使根據(jù)默認(rèn)采樣規(guī)則障本,它不需要被記錄教届。sampling.priority tag 提供這樣的?式。追蹤系統(tǒng)不保證?定采納這個(gè)參數(shù)驾霜,但是會(huì)盡可能的保留這條調(diào)?案训。
sampling.priority - integer
- 如果?于 0, 追蹤系統(tǒng)盡可能保存這條調(diào)?鏈
- 等于 0, 追蹤系統(tǒng)不保存這條調(diào)?鏈
- 如果此tag沒(méi)有提供,追蹤系統(tǒng)使???的默認(rèn)采樣規(guī)則
采樣速率
?產(chǎn)環(huán)境系統(tǒng)性能很重要粪糙,所以對(duì)于所有的請(qǐng)求都開(kāi)啟 Trace 顯然會(huì)帶來(lái)?較?的壓?强霎,另外,?量的數(shù)據(jù)也會(huì)帶來(lái)很?存儲(chǔ)壓?蓉冈。為此城舞,jaeger ?持設(shè)置采樣速率,根據(jù)系統(tǒng)實(shí)際情況設(shè)置合適的采樣頻率寞酿。
Jaeger 官?提供了多種采集策略家夺,使?者可以按需選擇使?:
- const,全量采集伐弹,采樣率設(shè)置0,1 分別對(duì)應(yīng)打開(kāi)和關(guān)閉;
- probabilistic拉馋,概率采集,默認(rèn)萬(wàn)份之?惨好,0~1之間取值;
- rateLimiting煌茴,限速采集,每秒只能采集?定量的數(shù)據(jù);
- remote日川,?種動(dòng)態(tài)采集策略蔓腐,根據(jù)當(dāng)前系統(tǒng)的訪問(wèn)量調(diào)節(jié)采集策略;
總結(jié)
- OpenTracing 設(shè)計(jì)類似于 Interface 抽象層設(shè)計(jì), 很適合不同方案兼容的場(chǎng)景, 并且具有很好的擴(kuò)展性, 后期改動(dòng)成本較小;
- 適合生產(chǎn)方案遷移 ;
在生產(chǎn)中使用 OpenTracing 需要考慮兼容不同協(xié)議的鏈路追蹤問(wèn)題,下一章和大家分享 支持 HTTP 和 gRPC 分布式鏈路追蹤 SDK 設(shè)計(jì) 龄句。
- By 斜桿青年