basictracer-go原始碼閱讀——event&propagation
event
這個event是有關span操作時產生的事件,這些事件統稱為:SpanEvent
,相關事件資料儲存格式如下:
// span建立時產生的事件
type EventCreate struct {OperationName string}
// 儲存tag操作時的事件
type EventTag struct{
Key string
Value interface{}
}
// 儲存baggage時的事件
type EventBaggage struct {
Kye, Value string
}
// 儲存日誌時的事件
type EventLogFields opentracing.LogRecord
// span Finish時的事件
type EventFinish RawSpan
// 針對這些事件資料型別,有一些span操作事件的方法,如下所示:
func (s *spanImpl) onCreate(opName string) {
if s.event !=nil {
s.event(EventCreate{OperationName: opName})
}
}
func (s *spanImpl) onTag(key string, value interface{}){
if s.event !=nil {
s.event(EventTag{Key: key, Value: value})
}
}
func (s *spanImpl) onLogFields(lr opentracing.LogRecord){
if s.event !=nil {
s.event(EventLogFields(lr))
}
}
func (s *spanImpl) onBaggage(key, value string) {
if s.event !=nil {
s.event(EventBaggage{Key: key, Value: value})
}
}
func (s *spanImpl) onFinish(sp RawSpan) {
if s.event!=nil {
s.event(EventFinish(sp))
}
}
// 以上這些都是針對span發生變化時的相關事件,這些可以記錄,也可以不記錄,取決於span.event是否被關注。
propagation
propagation主要是IPC/RPC跨程式資料傳輸。它包括上下文資訊攜帶的資料儲存格式,以及網路傳輸資料與本地資料格式轉換
我們在《basictracer原始碼閱讀——TracerImpl》的SpanImpl部分說過三種Baggage攜帶格式,包括TextMapPropagator、BinaryPropagator和AccessPropagator。
這三種資料型別存在兩種行為:Inject和Extract。用來進行SpanContext和Baggages之間的資料轉換
textMapPropagator
// 父級:Client或者Producer
func (p *textMapPropagator) Inject(spanContext opentracing.SpanContext,
opaqueCarrier interface{}) error{
sc, ok := spanContext.(SpanContext) // 獲取span本身相關資訊
// 這個無需指明或者猜測是否為HTTPHeader或者TextMap,因為這是opentracing-go底層透明實現Set和ForeachKey兩個方法
carrier, ok := opaqueCarrier.(opentracing.TextMapWriter)
// 把SpanContext需要上下文攜帶的資訊寫入到網路傳輸資料中
carrier.Set(fieldNameTraceID, strconv.FormatUint(sc.TraceID, 16))
carrier.Set(fieldNameSpanID, strconv.FormatUint(sc.SpanID, 16))
carrier.Set(fieldNameSampled, strconv.FormatBool(sc.Sampled))
// baggage資訊, 在SpanImpl章節說過,為了凸顯TraceID、SpanID和Sampled三個變數的價值,沒有放入到Baggage中。
for k, v := range sc.Baggage{
carrier.Set(prefixBaggage+k, v)
}
return nil
}
// 子級:Server或者Consumer
func (p *textMapPropagator) Extract(opaqueCarrier interface{}) (opentracing.SpanContext, error) {
// opaqueCarrier資料格式為HTTPHeader或者TextMap,這不用上層關心,因為這兩者已經實現了ForeachKey方法
carrier, ok := opaqueCarrier.(opentracing.TextMapReader)
carrier.ForeachKey(func(k, v string) error{
... // 根據Inject方法,解出對應的SpanContext相關資訊
// 包括TraceID、SpanID、Sampled和Baggage資訊
})
}
binaryPropagator
它是網路傳輸位元流,通過io.Reader和io.Writer進行流讀寫操作
// 父級:Client或者Producer
func (p *binaryPropagator) Inject(spanContext opentracing.SpanContext,
opaqueCarrier interface{}) error{
// 獲取span的SpanContext
sc, ok := spanContext.(SpanContext)
// 把SpanContext通過io.Writer, 採用protobuffer協議,
// 把它轉換為網路流中資料opaqueCarrier
carrier, ok := opaqueCarrier.(io.Writer)
// 然後把資料流通過pb協議和大端模式轉化為網路流carrier
state:=wire.TracerState{}
state.TraceId = sc.TraceID
state.SpanId = sc.SpanID
state.Sampled = sc.Sampled
state.BaggateItems = sc.Baggage // pb 支援map資料格式
b, err :=proto.Marshal(&state)
length:=uint32(len(b))
binary.Write(carrier, binary.BigEndian, &length)
carrier.Write(b)
// 這裡要注意的是,網路資料傳輸的格式是[length(b)+b]。讀取的時候,前uint32八個位元組為資料長度,偏移8位元組後,計算uint32儲存值,則是實際資料大小
// 在linux記憶體管理基本上都是這種資料傳輸形式,例如: 儲存struct{...}格式的資料流為:struct本身大小+struct中資料大小+struct資料體
}
// 子級:Server或者Consumer
func (p *binaryPropagator) Extract(opaqueCarrier interface{}) (opentracing.SpanContext, error) {
// 獲取binary網路流
carrier, ok := opaqueCarrier.(io.Reader)
binary.Read(carrier, binary.BigEndian, &length) //var length uint32
buf := make([]byte, length)
// 以上我們可以看到,Inject注入到網路流的資料大小,Extract解析時也是一樣的大小解析
carrier.Read(buf)
// buf是pb協議儲存, 並通過pb協議解析
ctx:=wire.TracerState{}
proto.Unmarshal(buf, &ctx)
return SpanContext{
TraceID: ctx.TraceId,
SpanID: ctx.SpanId,
Sampled: ctx.Sampled,
Baggage: ctx.BaggageItems,
}
}
accessPropagator
basictracer開放出了一個可供廠商自定義SpanContext與網路資料傳輸的儲存協議。
type DelegatingCarrier interface{
// 因為要遵循basicTracer的SpanContext,所以提供了SetState方法,讀寫traceID、SpanID和Sampled
SetState(traceID, spanID uint64, sampled bool)
State() (traceID, spanID uint64, sampled bool)
// 再就是需要對SpanContext和Carrier進行資料轉換
// 這兩個方法類似於TextMapReader和TextMapWriter介面
SetBaggageItem(key, value string)
GetBaggage(func(key, value string))
}
// 把SpanContext轉換為網路可傳輸的Carrier,依賴於具體實現
func (p *accessorPropagator) Inject(spanContext opentracing.SpanContext,
carrier interface{}) error{
// 轉換SpanContext到Carrier中
dc, ok := carrier.(DelegatingCarrier)
sc, ok := spanContext.(SpanContext)
dc.SetState...
for k, v:= range sc.Baggage{
dc.SetBaggageItem(k, v)
}
}
// 把網路傳輸資料Carrier轉換為SpanContext
func ( p *accessorPropagator) Extract(carrier interface{}) (opentracing.SpanContext, error) {
// 網路資料校驗是否為DeletingCarrier
dc, ok := carrier.(DelegatingCarrier)
// 獲取Span相關資訊
traceID, spanID, sampled:= dc.State()
sc := SpanContext{
TraceID: traceID,
SpanID: spanID,
Sampled: sampled,
Baggage: nil,
}
// 遍歷獲取baggage
dc.GetBaggage(func(k, v string) {
...
sc.Baggage[k] = v
})
return sc, nil
}
相關文章
- basictracer-go原始碼閱讀二——SpanGo原始碼
- basictracer-go原始碼閱讀——SpanRecorder與wireGo原始碼
- basictracer-go原始碼閱讀——examples(完結)Go原始碼
- 【原始碼閱讀】AndPermission原始碼閱讀原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)原始碼IDE
- 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
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- JDK原始碼閱讀:Object類閱讀筆記JDK原始碼Object筆記
- Laravel 原始碼閱讀 - QueueLaravel原始碼
- Vollery原始碼閱讀(—)原始碼
- 使用OpenGrok閱讀原始碼原始碼
- 如何閱讀Java原始碼?Java原始碼
- buffer 原始碼包閱讀原始碼
- 原始碼閱讀技巧篇原始碼
- 如何閱讀框架原始碼框架原始碼
- 再談原始碼閱讀原始碼
- Laravel 原始碼閱讀 - EloquentLaravel原始碼
- 如何閱讀jdk原始碼?JDK原始碼
- express 原始碼閱讀(全)Express原始碼
- Vuex原始碼閱讀分析Vue原始碼
- React原始碼閱讀:setStateReact原始碼
- ArrayList原始碼閱讀(增)原始碼
- ThreadLocal原始碼閱讀thread原始碼