Appdash原始碼閱讀——部分opentracing支援

cdh0805010118發表於2018-07-13

說過幾次 Appdash 的出生早於 OpenTracing 標準,為了支援 OpenTracing 標準,Appdash 做了一些擴充套件,但是我覺得它並不遵循 OpenTracing 標準, 新增了一個庫和簡單的補充。使用者既可以使用老版本的實現,也可以使用 OpenTracing 標準擴充套件的實現。

在 Appdash 原始碼有個 opentracing 目錄,這個是擴充套件實現。

OpenTracing

Appdash 基於部分 OpenTracing 標準的擴充套件實現,它是採用了 basictracer-go 庫進行相容實現的。這樣的改動代價是最小的。

Tracer

因為 Appdash 擴充套件的 OpenTracing 標準實現,是直接在 basictracer-go 實現的基礎上擴充套件的,所以相關的擴充套件都是針對 basictracer-go 的相關結構擴充套件

// 校驗是否實現了interface
var _ opentracing.Tracer = NewTracer(nil)

// 在basictracer-go庫中的Tracer引數,是採用的Options傳遞,所以Appdash的擴充套件也繼承了這種方式便於賦值, Appdash中的Options相關引數在basictracer-go中都能找到對應的 tracer 引數。
type Options struct {
    ShouldSample func(traceID uint64) bool

    Verbose bool

    Logger *log.Logger
}

// 預設的Tracer取樣是全量的, 每條tracer都儘量被儲存。
func DefaultOptions() Options {
    return Options {
        ShouldSample: func(_ uint64) bool { return true },
        Logger: newLogger(),
    }
}

func NewTracer(c appdash.Collector) opentracing.Tracer {
    return NewTracerWithOptions(c, DefaultOptions())
}

func NewTracerWithOptions(c appdash.Collector, options Options) opentracing.Tracer {
    opts := basictracer.DefaultOptions()
    opts.ShouldSample = options.ShouldSample
    opts.Recorder = NewRecorder(c, options)
    return basictracer.NewWithOptions(opts)
}

說到這裡,大家要想到一個問題,因為 basictracer-go 庫是完全遵循 OpenTracing 標準的,但是 Appdash 的 Span 設計是沒有遵循標準的。所以在 RPC/IPC 網路傳輸和本地 RawSpan 進行資料轉換時,以及網路資料解析時,都應該會遇到解析和資料轉換問題,所以 Tracer 的 Inject 和 Extract 需要重新實現,而不能使用 basictracer-go 庫。但是這裡並沒有看到相關實現,所以這個問題後面再看。::TODO

Recorder

appdash 擴充套件的 opentracing 中,Recorder 實現了 basictracer-go 庫的 Recorder interface

basictracer-go Recorder:

type SpanRecorder interface {
    RecordSpan(span RawSpan)
}

// basictracer-go庫已經給出了一個實現InMemorySpanRecorder

這裡 Appdash 也給出了一個實現 Recorder

type Recorder struct {
    collector appdash.Collector
    logOnce sync.Once
    varbose bool
    Log *log.Logger
}

// 新建Recorder
func NewRecorder(collector appdash.Collector, opts Options) *Recorder {
    ...
    return &Recorder{
        collector: collector,
        verbose: opts.Verbose,
        Log: opts.Logger,
    }
}

// 實現basictracer-go的Recorder interface
func (r *Recorder) RecorderSpan(sp basictracer.RawSpan) {
    ... // 校驗是否需要取樣
    //  baisctracer-go中的RawSpan資料轉換為Appdash中的Span資料
    spanID := appdash.SpanID {
        Span: appdash.ID(uint64(sp.Context.SpanID)),
        Trace: appdash.ID(uint64(sp.Context.TraceID)),
        Parent: appdash.ID(uint64(sp.ParentSpanID)),
    }

    // 儲存span name的event
    r.collectEvent(spanID, appdash.Span(sp.Operation))

    // 儲存span logs的event, 首先進行basictracer-go庫的logs序列化, 然後打上log event
    for _, log := range sp.Logs {
        logs, _ := materializeWithJSON(log.Fields)
        // collectEvent主要工作:進行event的序列化為Annotations, 然後儲存
        r.collectEvent(spanID, appdash.LogWithTimestamp(string(logs), log.Timestamp))
    }

    //  tags屬於非event事件, 儲存Tags
    for key, value := range sp.Tags {
        val := []byte(fmt.Sprintf("%+v", value))
        r.collectAnnotation(spanID, appdash.Annotation{Key: key, Value: val})
    }

    // Carrier攜帶資訊儲存
    for key, val := range sp.Context.Baggage {
        r.collectAnnotation(spanID, appdash.Annotation{Key: key, Value: []byte(val)})
    }

    // 儲存span的timespan event
    approxEndTime := sp.Start.Add(sp.Duration)
    r.collectEvent(spanID, appdash.Timespan{S: sp.Start, E: approxEndTime})
}

通過 RecorderSpan 方法可以知道,執行 basictracer.RawSpan 的 span 資訊儲存,需要大量的 TCP 網路傳輸,且每個事件都需要,非常耗時,且對於 collector 服務的效能也會有較大的影響。所以最好的方式是使用 Appdash 中的ChunkedCollector,進行 agent 本地儲存一定的 span 資訊後,在一次通過 tcp 傳送到 Collector 服務,進行儲存。

總結

針對上面提到的問題,Appdash 中擴充套件支援 OpenTracing 的實現,與自身 Span 資訊結構有很大的不同,無法對 SpanID 和 Annotations 進行 Inject 和 Extract,以及本地與 Baggage 資料轉換等,所以如果採用 OpenTracing 標準,同時 Collector 還是基於原來的 SpanID 和 Annotations 儲存,則需要進行 Recorder 層面的 basictracer-go RawSpan 資訊與 appdash Span 資訊轉換,這個正是由 SpanRecorder 方法實現的。

我們由此可以知道,Appdash 對 OpenTracing 標準的支援並不高,而且支援力度很弱,官方並沒有對 Appdash 做了大改動,只是做了一個基於 basictracer-go 擴充套件的一個小功能,沒有做很深入的工作。

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

相關文章