opentracing-go原始碼閱讀一
Tracer
Tracer Interface
type Tracer interface{
StartSpan(operationName string, opts ...StartSpanOption) Span
Inject(sm SpanContext, format interface{}, carrier interface{}) error
Extract(format interface{}, carrier interface{}) (SpanContext, error)
}
以上是實現一個tracer呼叫鏈跟蹤的最小子集,任何一個底層跟蹤系統的實現,如果實現了它,則可以把整個呼叫鏈串起來。
在Tracer interface中StartSpan方法傳參,傳入Span結構體引數,如:SpanReference列表
,建立時間
和Span Tag資訊
;其中:
SpanReference
列表:表示新建立的Span與【curr_depth+1】Span之間的關係,如FollowsFrom和ChildOf;注意,這個是RPC/IPC跨程式呼叫關係,所以SpanContext表示OpenTracing標準中可以攜帶Baggage資訊;
Tags
: 型別:map[string]interface{}, 生命週期:span執行單元;
因為SpanReference
, StartTime
和 Tags
三個變數為StartSpanOptions引數,所以必須實現StartSpanOption介面中Apply方法。 這個StartSpanOptions代表Span的必要引數子集。
globaltracer
每一個微服務都會有一個globaltracer值, 我們通過SetGlobalTracer方法設定業務系統採用的分散式跟蹤系統產品;一個產品會有1個到多個微服務,則每個微服務都需要指定全域性Tracer變數值,這一個Tracer就代表一個遵循OpenTracing標準的廠商產品,在一個產品中,所有微服務的Tracer變數值都必須指向同一個廠商產品,否則,分散式跟蹤系統服務生效。
其中,我們可以通過GlobalTracer()
方法獲取微服務中的Tracer值;
如果我們沒有對Tracer進行初始化,則意味著沒有指定採用哪個廠商的分散式跟蹤系統產品,則預設情況下,採用opentracing-go底層標準下的預設空Tracer——NoopTracer,這個是在實現OpenTracing標準API庫時,必須要做的,因為如果業務系統採用的元件或者第三方庫中有探針,如果不實現空的Tracer,則直接報錯,無法使用,從而導致Tracer與業務強耦合了。
NoopTracer
微服務中預設的空Tracer,包含:NoopTracer、noopSpan和noopSpanContext三個變數值;其中:
- noopSpanContext是在SpanReference中使用,它代表SpanContext上下文傳輸tracer時的span獲取。並攜帶Baggage資訊;
ForeachBaggageItem(func(k, v string) bool){}
- noopSpan實現了Span interface。方法實現全部為空;
- noopTracer實現了Tracer interface,包括StartSpan、Inject和Extract三個方法;全部為空實現;
微服務中,NoopTracer是Tracer未指定時的預設實現,不會在Tracer中生成任何資料,也不會產生Tracer呼叫鏈路;
Span
Span interface
type Span interface{
// Span執行單元結束
Finish()
// 帶結束時間和日誌記錄列表資訊,Span執行單元結束;也就是說結束時間可以業務指定; 日誌列表也可以直接新增; 目前還不知道這個的使用場景;
FinishWithOptions(opts FinishOptions)
// 把Span的Baggage封裝成SpanContext
Context() SpanContext
// 設定Span的操作名稱
SetOperationName(operationName string) Span
// 設定Span Tag
SetTag(key string, value interface{}) Span
// 設定Span的log.Field列表;
// span.LogFields(
// log.String("event", "soft error"),
// )
LogFields(fields ...log.Field)
// key-value列表={"event": "soft error", "type": "cache timeout", "waited.millis":1500}
LogKV(alternatingKeyValues ...interface{})
// LogFields與LogKV類似,只是前者已封裝好;
// 設定span的Baggage:key-value, 用於跨程式上下文傳輸
SetBaggageItem(restrictedKey, value string) Span
// 通過key獲取value;
BaggageItem(restrictedKey string) string
// 獲取Span所在的呼叫鏈tracer
Tracer() Tracer
// 廢棄,改用LogFields 或者 LogKV
LogEvent(event string)
// 同上
LogEventWithPayload(event string, payload interface{});
// 同上
Log(data LogData)
}
SpanContext
首先,需要看一小段程式碼:
package main
import "fmt"
type Fun struct{}
func main() {
var fun1, fun2 = Fun{}, Fun{}
if fun1 == fun2 {
fmt.Println("fun1==fun2")
} else {
fmt.Println("fun1!=fun2")
}
}
// 執行結果:fun1==fun2
// 比較兩個值是否相等,取決於:值和型別,都相等這表示相同;
Context用於上下文資料傳輸使用,在OpenTracing標準中,Span之間跨程式呼叫時,會使用SpanContext傳輸Baggage攜帶資訊。通過context標準庫實現;如:
type contextKey struct{}
var activeSpanKey = contextKey{}
// 封裝span到context中
func ContextWithSpan(ctx context.Context, span Span) context{
return context.WithValue(ctx, activeSpanKey, span)
}
// 從ctx中通過activeSpanKey取出Span,這裡可以看到不同服務的activeSpanKey值,是相同的,上面的DEMO可以說明。
func SpanFromContext(ctx context.Context) Span{
val:=ctx.Value(activeSpanKey)
if sp, ok := val.(Span); ok{
return sp
}
return nil
}
通過context上下文的activeSpanKey,我們可以獲得Span,並建立新的span,如:
func startSpanFromContextWithTracer(ctx context.Context, tracer Tracer, operationName string, opts ...StartSpanOption) (Span, context.Context){
// 首先從上下文看是否能夠獲取到span,如果獲取不到,再建立tracer和span;
if parentSpan:= SpanFromContext(ctx); parentSpan !=nil {
opts = append(opts, ChildOf(parentSpan.Context()))
}
span := tracer.StartSpan(operationName, opts...)
return span, ContextWithSpan(ctx, span)
}
參考資料
相關文章
- opentracing-go原始碼閱讀——資訊攜帶Go原始碼
- opentracing-go原始碼閱讀——Log儲存(完結篇)Go原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- Kingfisher原始碼閱讀(一)原始碼
- YYModel原始碼閱讀(一)原始碼
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- 如何閱讀一份原始碼?原始碼
- 逐行閱讀redux原始碼(一) createStoreRedux原始碼
- ReactorKit原始碼閱讀React原始碼
- AQS原始碼閱讀AQS原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- NGINX原始碼閱讀Nginx原始碼
- Mux 原始碼閱讀UX原始碼
- HashMap原始碼閱讀HashMap原始碼
- fuzz原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- express 原始碼閱讀Express原始碼
- muduo原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- 【詳解】ThreadPoolExecutor原始碼閱讀(一)thread原始碼
- 原始碼閱讀:Masonry(一)——從使用入手原始碼
- 原始碼閱讀:SDWebImage(一)——從使用入手原始碼Web
- Spring原始碼閱讀學習一Spring原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- Laravel 原始碼閱讀 - QueueLaravel原始碼
- Vollery原始碼閱讀(—)原始碼
- 使用OpenGrok閱讀原始碼原始碼
- 如何閱讀Java原始碼?Java原始碼
- buffer 原始碼包閱讀原始碼
- 原始碼閱讀技巧篇原始碼
- 如何閱讀框架原始碼框架原始碼
- 再談原始碼閱讀原始碼