opentracing-go原始碼閱讀——資訊攜帶

cdh0805010118發表於2018-07-03

Propagation

  1. 跨程式的 trace 資訊傳輸通過 SpanContext 傳遞 baggage 資訊,把 baggage 儲存到 context 上下文中,則需要 key:value, 這個 key 決定了 value 值和 value 型別;
  2. key:value 儲存到 context 中,需要藉助於讀寫操作;notice: 這個借鑑了io.Reader和io.Writer等思想,通過組合模式使得實現變得更加靈活
  3. 目前支援三種 key 值,對應三種 value 值型別,分別是:byte 流、TextMap 和 http.Header;其中後兩者都是 map 結構;只是 RPC 或者 Web 時,用 http.Header,其他使用 TextMap 或者 byte 流;

Baggage 讀寫

目前支援三種型別的 value,BuiltinFormat={Binary=0, TextMap=1, HTTPHeaders=2}, 對於第一種位元流方式,直接通過io.Readerio.Writer方式即可;

對於後兩者的讀寫操作:

type TextMapWriter interface {
    Set(key, val string)
}

type TextMapReader interface {
    ForeachKey(handler func(key, value string) error) error
}

從 TextMapReader 來看,ForeachKey 的傳參型別是一個函式,使得讀取交給了具體的 trace 系統實現,靈活度高;

Baggage 的 TextMap 和 Http.Header 兩種儲存方法,分別實現了上面兩個介面:

// TextMap
type TextMapCarrier map[string]string

func (c TextMapCarrier) ForeachKey(handler func(key, val string) error) error{
    for k, v := range c{
        if err := handler(k, v); err!=nil{
            return err
        }
    }
    return nil
}

func (c TextMapCarrier) Set(key, val string){
    c[key] = val
}


// Http.Header
type HTTPHeadersCarrier http.Header

func (c HTTPHeadersCarrier) Set(key, val string){
    h:= http.Header(c)
    h.Add(key, val)
}

func (c HTTPHeadersCarrier) ForeachKey(handler func(key, value string) error) error{
    for k, vals:= range c{
        for _, v := range vals{
            if err:= handler(k, v); err !=nil{
                return errr
            }
        }
    }
    return nil
}

ext/tags

根據 OpenTracing 標準,《OpenTracing APIs》中的資料約定模組,已經在標準中整合了一些常用的 Span Tag。在 opentracing-go 庫中,也就整合了這一部分子集,如:

SpanKind:"span.kind" 代表有關RPC、WEB、訊息釋出與訂閱等關係;這類Span Tag值={"Client", "Server", "Producer", "Consumer"}

第三方庫或者元件 Component: "component"; 這類span tag值表示呼叫的第三方庫或者元件名稱;

取樣率:SamplingPriority: "sampling.priority"; 這類span tag值大於或者等於0,當value>0時,儘量保留這條trace;否則,丟棄這條trace;

peer:這類span tag表示對等的相關資訊,也即呼叫方上游或者下游的相關服務資訊;具體有:
PeerService: "peer.service"; tag值為服務名;
PeerAddress: "peer.address"; tag值為服務連線地址;
PeerHostName:"peer.hostname"; tag值為主機名;
PeerHostIPv4, PeerHostIPv6: "peer.ipv4"和"peer.ipv6"; tag值為IP地址
PeerPort: "peer.port";tag值為服務埠;

HTTP相關tags列表:
HTTPUrl: "http.url"; tag值為http url;
HTTPMethod:"http.method"; tag值為http.method={"get", "post", "delete", "head", ...};
HTTPStatusCode: "http.status_code"; tag值為http.status_code={403, 404, 200, 502, ...};

DB相關tags列表:
DBInstance: "db.instance", tag值為db例項名稱;
DBStatement:"db.statement", tag值為db sql語句;
DBType: "db.type", tag值為db type = {"redis", "mysql", "progresql", ...};
DBUser: "db.user", tag值為訪問db的使用者名稱
// 這裡不給出DBPassword是因為,passwd屬於非常敏感資訊;

// message bus 訊息匯流排tag:
MessageBusDestination: "message_bus.detination", tag值為address;
訊息匯流排,我還不太瞭解;

// error tag: 表示span執行單元過程是否有業務系統發生錯誤,true|false
Error: "error", tag值:true|false;

我們注意到,當跟蹤某一類 tag A 時,如果這類 tag 存在多個指標;這 A.a, A.b, ..., A.z 等方式是非常友好的,便於識別含義,且不會衝突;

上面的這些 tag 都會有相應的讀寫操作:Set, 但是目前沒有發現 Tag 讀取操作, 大多數 tag set 操作類似於:

func (tag xxxTagName) Set(span opentracing.Span, value xxtype){
    span.Set(string(tag), value)
}

這裡說明一點: 對於已知的 TagName 和可列舉的 value,直接使用 StartSpanOptions 中的 Apply 方法即可,如:span.kind; 其他的通過 TagName 型別自帶的 Set 實現儲存,這些 tag 資料都是儲存在 StartSpanOptions 的 tags 中

參考資料

opentracing-go

更多原創文章乾貨分享,請關注公眾號
  • opentracing-go原始碼閱讀——資訊攜帶
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章