basictracer原始碼閱讀——TracerImpl

cdh0805010118發表於2018-07-04

首先,我們先說一下,opentracing-language 是支撐 OpenTracing 標準的最小子集,而 basictracer-language 是對這最小子集的基本實現;我們可以不使用 basictracer,直接廠商作為最小子集的擴充套件就行;所以 basictracer 是其中一個擴充套件子集,方便大家做 tracer 設計參考;

Tracer

basictracer 的 Tracer 擴充套件了一些引數集 options,支援以下操作:

  1. ShouldSample 函式:可以根據一定規則,對匹配到的 tracer 則發起跟蹤;
  2. TrimUnsampledSpans 布林值:當某個 tracer 被丟棄時,直接不會在這條鏈路上的各個 span 產生資料儲存;
  3. Recorder:當 Span 執行單元結束被 Finish 後,則形成特定 span recorder 並通過 agent 傳送到 collector 儲存;
  4. NewSpanEventListener:trace 事件監聽,// ::TODO 後面更新
  5. DropAllLogs 布林值: 如果 NewSpanEventListener 被設定了,則該布林值變數無效;否則,如果為 True,則所有的 Span Logs 都指向 noop 操作
  6. MaxLogsPerSpan:因為 span logs 在 OpenTracing 標準中只是說減少 Baggage 資訊儲存,因為跨 IPC/RPC 網路延遲大,這裡是指 span 本地 logs 儲存儘量少,畢竟 OpenTracing 不是分散式日誌管理系統。
  7. EnableSpanPool 布林值:複用已經 finish 的 span 列表,減少記憶體使用;

預設的 Options:

  1. ShouldSample 函式為 mod 64,盡最大能力儲存該條 trace;
  2. 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,並且提供了額外的操作和儲存:

  1. TraceID、SpanID 和 Sampled,沒有儲存到 Baggage 的 map 中,而是單獨拎出來,含義更加鮮明,易於理解;
  2. 提供了 SpanContext 的 WithBaggageItem 方法,裝載 Baggage 攜帶資訊;

同時,這裡的 WithBaggageItem 方法,我有兩個疑問

  1. SpanContext 的生命週期在於本地 Span,在 RPC/IPC 時會通過 TextMapCarriers、HTTPHeaderCarriers 和 BinaryCarriers 進行攜帶,同一個 Span 執行單元不會併發,所以應該不需要新建一個 newBaggage 儲存,寫入然後再賦予給 SpanContext 中的 Baggage;
  2. 這個方法為何還要返回值 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 方法,其中,後兩個方法:

  1. Inject 方法根據 RPC/IPC 傳輸資料格式,來進行具體的 Inject 操作;如:format 為 TextMap 或者 HTTPHeader,則通過 textMapPropagator 進行 SpanContext 的資料轉換;如果是 binary,則通過 binaryPropagator 進行資料轉換;如果是廠商自己實現,則通過 accessPropagator 委託實現;notice: TextMap和HTTPHeader雖然儲存資料格式不同,但是opentracing-go的TextMapReader和TextMapWriter已經遮蔽了資料格式儲存方式
  2. 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,減少記憶體使用;

參考資料

basictracer-go

更多原創文章乾貨分享,請關注公眾號
  • basictracer原始碼閱讀——TracerImpl
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章