Appdash原始碼閱讀——部分opentracing支援
說過幾次 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 擴充套件的一個小功能,沒有做很深入的工作。
- 加微信實戰群請加微信(註明:實戰群):gocnio
相關文章
- Appdash原始碼閱讀——reflectAPP原始碼
- Appdash原始碼閱讀——RecentStore和LimitStoreAPP原始碼MIT
- Appdash原始碼閱讀——Annotations與EventAPP原始碼
- Appdash原始碼閱讀——Recorder與CollectorAPP原始碼
- Appdash原始碼閱讀——Store儲存APP原始碼
- Appdash原始碼閱讀——Tracer&SpanAPP原始碼
- opentracing-go原始碼閱讀一Go原始碼
- Appdash原始碼閱讀——Tracer&SpanAPP原始碼
- opentracing-go原始碼閱讀——資訊攜帶Go原始碼
- opentracing-go原始碼閱讀——Log儲存(完結篇)Go原始碼
- redux 和 react-redux 部分原始碼閱讀ReduxReact原始碼
- 【原始碼閱讀】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原始碼
- Vue2.x 響應式部分原始碼閱讀記錄Vue原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- JDK原始碼閱讀:String類閱讀筆記JDK原始碼筆記
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- 如何閱讀Java原始碼?Java原始碼
- buffer 原始碼包閱讀原始碼