basictracer原始碼閱讀——TracerImpl
首先,我們先說一下,opentracing-language 是支撐 OpenTracing 標準的最小子集,而 basictracer-language 是對這最小子集的基本實現;我們可以不使用 basictracer,直接廠商作為最小子集的擴充套件就行;所以 basictracer 是其中一個擴充套件子集,方便大家做 tracer 設計參考;
Tracer
basictracer 的 Tracer 擴充套件了一些引數集 options,支援以下操作:
- ShouldSample 函式:可以根據一定規則,對匹配到的 tracer 則發起跟蹤;
- TrimUnsampledSpans 布林值:當某個 tracer 被丟棄時,直接不會在這條鏈路上的各個 span 產生資料儲存;
- Recorder:當 Span 執行單元結束被 Finish 後,則形成特定 span recorder 並通過 agent 傳送到 collector 儲存;
- NewSpanEventListener:trace 事件監聽,// ::TODO 後面更新
- DropAllLogs 布林值: 如果 NewSpanEventListener 被設定了,則該布林值變數無效;否則,如果為 True,則所有的 Span Logs 都指向 noop 操作
- MaxLogsPerSpan:因為 span logs 在 OpenTracing 標準中只是說減少 Baggage 資訊儲存,因為跨 IPC/RPC 網路延遲大,這裡是指 span 本地 logs 儲存儘量少,畢竟 OpenTracing 不是分散式日誌管理系統。
- EnableSpanPool 布林值:複用已經 finish 的 span 列表,減少記憶體使用;
預設的 Options:
- ShouldSample 函式為 mod 64,盡最大能力儲存該條 trace;
- MaxLogsPerSpan 值為 100
建立 Tracer 例項的方法有兩種:NewWithOptions 和 New。前者傳入 Options 引數,最常用;後者使用預設的 Options,傳入 Recorder 來儲存 Tracer
SpanContext Impl
basictracer 中的 SpanContext 實現了 opentracing-go 中的 SpanContext interface, 本地資料儲存格式如下:
type SpanContext struct {
TraceID uint64
SpanID uint64
Sampled bool
Baggage map[string]string
}
func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool){
for k, v := range c.Baggage{
if !handler(k, v){
break
}
}
}
func (c SpanContext) WithBaggageItem(key, val string) SpanContext{
var newBaggage map[string]string
if c.Baggage ==nil{
newBaggage = map[string]string{
key: val,
}
} else {
newBaggage = make(map[string]string, len(c.Baggage)+1)
for k, v := range c.Baggage{
newBaggage[k] = v
}
newBaggage[key] = val
}
return SpanContext{c.TraceID, c.SpanID, c.Sampled, newBaggage}
}
上面的 SpanContext 實現了 opentracing-go 的 SpanContext interface,並且提供了額外的操作和儲存:
- TraceID、SpanID 和 Sampled,沒有儲存到 Baggage 的 map 中,而是單獨拎出來,含義更加鮮明,易於理解;
- 提供了 SpanContext 的 WithBaggageItem 方法,裝載 Baggage 攜帶資訊;
同時,這裡的 WithBaggageItem 方法,我有兩個疑問:
- SpanContext 的生命週期在於本地 Span,在 RPC/IPC 時會通過 TextMapCarriers、HTTPHeaderCarriers 和 BinaryCarriers 進行攜帶,同一個 Span 執行單元不會併發,所以應該不需要新建一個 newBaggage 儲存,寫入然後再賦予給 SpanContext 中的 Baggage;
- 這個方法為何還要返回值 SpanContext,如果不返回值,我覺得沒什麼問題;
TracerImpl
tracerImpl 實現了 Tracer 最基本的最小子集 OpenTracing 標準——opentracing-go;
type tracerImpl struct{
// tracer的擴充套件引數集
options Options
// TextMap和HTTPHeader
textPropagator *textMapPropagator
// 位元流
binaryPropagator *binaryPropagator
// 如果廠商打算基於basictracer進行擴充套件,這裡提供一個Baggage上下文資訊傳輸的interface介面方法列表
accessorPropagator *accessorPropagator
}
tracerImpl 中的 StartSpan, Inject 和 Extract 方法,其中,後兩個方法:
- Inject 方法根據 RPC/IPC 傳輸資料格式,來進行具體的 Inject 操作;如:format 為 TextMap 或者 HTTPHeader,則通過 textMapPropagator 進行 SpanContext 的資料轉換;如果是 binary,則通過 binaryPropagator 進行資料轉換;如果是廠商自己實現,則通過 accessPropagator 委託實現;
notice: TextMap和HTTPHeader雖然儲存資料格式不同,但是opentracing-go的TextMapReader和TextMapWriter已經遮蔽了資料格式儲存方式
- Extract 方法,同上,只是反方向,把 RPC/IPC 網路傳輸資料通過 SpanContext 資料儲存格式進行資料轉換;
對於 StartSpan 方法的程式碼片段,我有個疑問:
ReferencesLoop:
for _, ref := range opts.References {
switch ref.Type {
case opentracing.ChildOfRef,
opentracing.FollowsFromRef:
refCtx := ref.ReferencedContext.(SpanContext)
sp.raw.Context.TraceID = refCtx.TraceID
sp.raw.Context.SpanID = randomID()
sp.raw.Context.Sampled = refCtx.Sampled
sp.raw.ParentSpanID = refCtx.SpanID
if l := len(refCtx.Baggage); l > 0 {
sp.raw.Context.Baggage = make(map[string]string, l)
for k, v := range refCtx.Baggage {
sp.raw.Context.Baggage[k] = v
}
}
break ReferencesLoop
}
}
以上我們可以看到,當前要建立的 Span 與其關聯的 References 列表,只取出了首個 Reference,且 ParentSpanID 的賦值,無論是 ChildOf 或者 FollowsFrom 都可以;我認為兄弟關係不可以成為 ParentSpanID;
備註:在SpanReferences列表中,只會存在[0,1]個ParentSpan和[0, N]個兄弟span
在 Options 中有個屬性變數:EnableSpanPool 布林值,如果為 true,則使用 sync.Pool 臨時物件池,當服務關閉後,則釋放這些記憶體空間;它用來獲取可用的 Span,減少記憶體使用;
參考資料
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- basictracer-go原始碼閱讀二——SpanGo原始碼
- basictracer-go原始碼閱讀——event&propagationGo原始碼
- basictracer-go原始碼閱讀——SpanRecorder與wireGo原始碼
- basictracer-go原始碼閱讀——examples(完結)Go原始碼
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)原始碼IDE
- ReactorKit原始碼閱讀React原始碼
- Vollery原始碼閱讀(—)原始碼
- NGINX原始碼閱讀Nginx原始碼
- ThreadLocal原始碼閱讀thread原始碼
- 原始碼閱讀-HashMap原始碼HashMap
- Runtime 原始碼閱讀原始碼
- RunLoop 原始碼閱讀OOP原始碼
- AmplifyImpostors原始碼閱讀原始碼
- stack原始碼閱讀原始碼
- CountDownLatch原始碼閱讀CountDownLatch原始碼
- fuzz原始碼閱讀原始碼
- HashMap 原始碼閱讀HashMap原始碼
- delta原始碼閱讀原始碼
- AQS原始碼閱讀AQS原始碼
- Mux 原始碼閱讀UX原始碼
- ConcurrentHashMap原始碼閱讀HashMap原始碼
- HashMap原始碼閱讀HashMap原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- JDK原始碼閱讀:String類閱讀筆記JDK原始碼筆記
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- 如何閱讀Java原始碼?Java原始碼
- buffer 原始碼包閱讀原始碼
- 使用OpenGrok閱讀原始碼原始碼
- express 原始碼閱讀(全)Express原始碼
- Kingfisher原始碼閱讀(一)原始碼
- 如何閱讀框架原始碼框架原始碼
- 如何閱讀jdk原始碼?JDK原始碼
- ArrayList原始碼閱讀(增)原始碼
- snabbdom 原始碼閱讀分析原始碼
- Appdash原始碼閱讀——reflectAPP原始碼