關於opentracing的簡單介紹
什麼是opentracing?
OpenTracing的數據模型
opentracing中的跟蹤鏈由span隱性的定義。可別的,跟蹤鏈可以認爲是span的有向無環圖(DAG),spans之間的邊稱爲References。
以下是一個跨度的例子圖:
解析爲時間軸可視化的圖:
每個span封裝以下的狀態:
(1)操作名稱
(2)開始時間戳
(3)完成時間戳
(4)一組零個或多個key:value跨度標籤。 鍵必須是字符串。 值可以是字符串,布爾型或數字類型。
(5)一組零個或多個跨度日誌,每個跨度日誌本身就是與時間戳配對的key:value映射。 鍵必須是字符串,儘管值可以是任何類型。 並非所有OpenTracing實現都必須支持每種值類型。
(6)SpanContext(請參見下文)
(7)零個或多個因果相關的Span的引用(通過那些相關Span的SpanContext)
每個SpanContext封裝以下狀態
(1)引用跨過程span的不同process所需的任何OpenTracing實現依賴狀態(例如,跟蹤和跨度ID)
(2)Baggage Items,僅是k:v鍵值對,跨越process的邊界
span之間的References
Span可以引用因果相關的零個或多個其他SpanContext。
Span可以引用因果相關的零個或多個其他SpanContext。 OpenTracing當前定義了兩種類型的引用:ChildOf和FollowsFrom。 兩種參考類型都專門爲子Span和父Span之間的直接因果關係建模。 將來,OpenTracing可能還會支持具有非因果關係的Span的引用類型(例如,批處理在一起的Span,卡在同一隊列中的Span等)。
ChildOf:
span可以是父親span的子,在ChildOf的reference中父的span某些情況下依賴於子的span,以下所有內容將構成ChildOf關係:
(1)代表RPC的服務器端的Span可以是代表該RPC客戶端的Span的ChildOf
(2)表示SQL插入的Span可以是代表ORM保存方法的Span的ChildOf
(3)許多同時執行(可能是分佈式的)工作的Span可能全部是單個父Span的ChildOf,它合併了在截止期限內返回的所有子代的結果
這些對於作爲父級ChildOf的子級來說都是有效的時序圖。
FollowsFrom:
一些父Span完全不依賴於其子Span的結果。 在這些情況下,我們僅說因果關係上子Span從父Span跟隨。 有許多不同的FollowsFrom參考子類別,在將來的OpenTracing版本中,它們可能會更正式地加以區分。
這些對於“FollowsFrom”的孩子來說都是有效的時序圖。
The OpenTracing API
OpenTracing規範中包含三種關鍵且相互關聯的類型:Tracer,Span和SpanContext。
粗略地說,每種行爲在典型的編程語言中都成爲一種“方法”,儘管由於類型重載等原因,它實際上可能是一組相關的同級方法。
--------------------------------------------------------------Tracer--------------------------------------------------
Tracer接口創造span並且瞭解如何將span跨process序列化和反序列化他們。Tracer接口具有以下功能。
1.開始一個新的span:
必要參數:
操作名稱,例子:get_account
可選參數:
1.零個或多個對相關SpanContext的引用,如果可能的話,包括ChildOf和FollowsFrom引用類型的簡寫。
2.可選的顯式開始時間戳記; 如果省略,則默認使用當前的walltime
3.零個或多個標籤
返回已經啓動(但尚未完成)的Span實例
2.將SpanContext注入載體
必要參數:
1.SpanContext實例
2.格式描述符(通常但不一定是字符串常量),它告訴Tracer實現如何在載波參數中對SpanContext進行編碼
3.載體,其類型由格式決定。 Tracer實現將根據格式在此載體對象中對SpanContext進行編碼。
3.從運營商中提取SpanContext
必要參數:
1.格式描述符(通常但不一定是字符串常量),它告訴Tracer實現如何從載波參數中解碼SpanContext
2.載體,其類型由格式決定。 Tracer實現將根據格式從該載體對象解碼SpanContext。
當通過Tracer啓動新的Span時,返回適合用作參考的SpanContext實例。
注意:注射和提取所需的格式
注入和提取均依賴於可擴展的格式參數,該參數指示關聯的“載體”的類型以及如何在該載體中編碼SpanContext。 所有Tracer實現都必須支持以下所有格式。
1.文本映射(Text Map):任意字符串到字符串的映射,其中鍵和值的字符集不受限制
2.HTTP標頭(Http Headers):具有適用於HTTP標頭(例如RFC 7230)的鍵和值的字符串到字符串的映射。 在實踐中,由於HTTP頭的處理方式存在“多樣性”,因此強烈建議Tracer實現使用有限的HTTP頭密鑰空間並保守地轉義值。
3.二進制(二進制):表示SpanContext的(單個)任意二進制Blob
--------------------------------------------------------------Span--------------------------------------------------
可以做的事情:
1.檢索跨度SpanContext
2.覆蓋操作名稱
3.完成跨度
4.設置跨度標籤
5.記錄結構化數據
6.Set a baggage item
7.Get a baggage item
----------------------------------------------------------SpanContext----------------------------------------------
SpanContext不僅僅是通用OpenTracing層上的有用功能,而是更多的“概念”。 也就是說,這對於OpenTracing實現至關重要,並且確實提供了自己的瘦API。 大多數OpenTracing用戶在啓動新的Span或向某些傳輸協議注入/從某些傳輸協議提取跟蹤時,僅通過引用與SpanContext進行交互。
在OpenTracing中,我們強制SpanContext實例是不可變的,以避免圍繞Span精加工和引用的複雜生命週期問題。
----------------------------------------------------------NoopTracer----------------------------------------------
所有OpenTracing語言API也必須提供某種NoopTracer實現,該實現可用於標記控制OpenTracing或注入對測試無害的東西(等等)。 在某些情況下(例如Java),NoopTracer可能位於其自己的打包工件中。
opentracing-go使用實例
初始化tracer
func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: samplerType,
Param: samplerParam,
},
Reporter: &config.ReporterConfig{
LocalAgentHostPort: "127.0.0.1:6831",
LogSpans: true,
},
}
tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("Init failed: %v\n", err))
}
return tracer,closer
}
客戶端注入信息
func makeSomeRequest(ctx context.Context) ... {
if span := opentracing.SpanFromContext(ctx); span != nil {
httpClient := &http.Client{}
httpReq, _ := http.NewRequest("GET", "http://myservice/", nil)
// Transmit the span's TraceContext as HTTP headers on our
// outbound request.
opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(httpReq.Header))
resp, err := httpClient.Do(httpReq)
...
}
...
}
服務端解析信息
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
var serverSpan opentracing.Span
appSpecificOperationName := ...
wireContext, err := opentracing.GlobalTracer().Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
// Optionally record something about err here
}
// Create the span referring to the RPC client if available.
// If wireContext == nil, a root span will be created.
serverSpan = opentracing.StartSpan(
appSpecificOperationName,
ext.RPCServerOption(wireContext))
defer serverSpan.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
...
}
以下是簡化他人的客戶端代碼
package main
import (
// "context"
"fmt"
"github.com/uber/jaeger-client-go"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"github.com/opentracing/opentracing-go"
// "github.com/opentracing/opentracing-go/log"
"github.com/uber/jaeger-client-go/config"
)
const (
URL = "http://localhost:8080"
LIST_API = "/getList"
RESULT_API = "/getResult"
)
var (
flag = make(chan bool)
)
func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: samplerType,
Param: samplerParam,
},
Reporter: &config.ReporterConfig{
LocalAgentHostPort: "127.0.0.1:6831",
LogSpans: true,
},
}
tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("Init failed: %v\n", err))
}
return tracer,closer
}
func sendRequest(req *http.Request) {
go func(req *http.Request) {
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("Do send requst failed(%s)\n", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("ReadAll error(%s)\n", err)
return
}
if resp.StatusCode != 200 {
return
}
fmt.Printf("Response:%s\n", string(body))
flag <- true
}(req)
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Argument error(getlist or getresult number) ")
os.Exit(1)
}
tracer, closer := TraceInit("CS-tracing", "const", 1)
defer closer.Close()
opentracing.SetGlobalTracer(tracer)
span := tracer.StartSpan(fmt.Sprintf("%s trace", os.Args[1]))
span.SetTag("trace to", os.Args[1])
defer span.Finish()
api := ""
var err error
if os.Args[1] == "getlist" {
api = LIST_API
}
reqURL := URL + api
req, err := http.NewRequest("GET", reqURL, nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
span.Tracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header),
)
if os.Args[1] == "getresult" {
q := req.URL.Query()
q.Add("num", os.Args[2])
req.URL.RawQuery = q.Encode()
}
sendRequest(req)
<-flag
}
對應的服務端代碼
package main
import (
"fmt"
"github.com/uber/jaeger-client-go"
"io"
"net/http"
"github.com/uber/jaeger-client-go/config"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
var (
tracer opentracing.Tracer
)
func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
cfg := &config.Configuration{
ServiceName: serviceName,
Sampler: &config.SamplerConfig{
Type: samplerType,
Param: samplerParam,
},
Reporter: &config.ReporterConfig{
LocalAgentHostPort: "127.0.0.1:6831",
LogSpans: true,
},
}
tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("Init failed: %v\n", err))
}
return tracer,closer
}
func GetListProc(w http.ResponseWriter, req *http.Request) {
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
span := tracer.StartSpan("GetListProc", ext.RPCServerOption(spanCtx))
defer span.Finish()
fmt.Println("Get request getList")
respList := []string{"l1", "l2", "l3", "l4", "l5"}
respString := ""
for _, v := range respList {
respString += v + ","
}
fmt.Println(respString)
io.WriteString(w, respString)
}
func main() {
var closer io.Closer
tracer, closer = TraceInit("Trace-Server", "const", 1)
defer closer.Close()
http.HandleFunc("/getList", GetListProc)
http.ListenAndServe(":8080", nil)
}
參考文件:
https://opentracing.io/specification/(概念)
https://github.com/opentracing/opentracing-go
https://segmentfault.com/a/1190000021273636?utm_source=tag-newest
https://blog.csdn.net/liyunlong41/article/details/88043604
https://blog.csdn.net/liyunlong41/article/details/87932953