Jaeger全鏈路go實現,包含http和消息隊列的鏈式傳遞
完整代碼
https://github.com/werbenhu/jaeger-go-demo
同時,我提供了一個封裝好的版本,更方便使用請參考
https://github.com/werbenhu/jaeger-go
本地鏈路
本地鏈路,不涉及到trace-id跨服務器傳遞。
通過opentracing.StartSpanFromContext即可完成,父span和子span關系
如下代碼:
tracer := opentracing.GlobalTracer()
//創建根span
rootSpan, rootCtx := opentracing.StartSpanFromContext(context.Background(), "root-span")
//...
//創建子span
subSpan, subCtx := opentracing.StartSpanFromContext(rootCtx, "sub-span")
//...
//...
subSpan.Finish()
//...
rootSpan.Finish()
//...
http跨服務
http跨服務需要解決trace-id傳遞的問題,參考下面的文檔
https://github.com/opentracing/opentracing-go
https://opentracing.io/docs/overview/inject-extract/
客戶端請求的時候需要將trace-id放到header中

client := &http.Client{}
req, _ := http.NewRequest("GET","http://localhost:9002/server_two",nil)
// refer to https://github.com/opentracing/opentracing-go
// refer to https://opentracing.io/docs/overview/inject-extract/
tracer := opentracing.GlobalTracer()
// 生成一個請求的span
clientSpan, clientCtx := opentracing.StartSpanFromContext(ctx, "http-one-req")
carrier := opentracing.HTTPHeadersCarrier{}
tracer.Inject(clientSpan.Context(), opentracing.HTTPHeaders, carrier)
// 將當前span的trace-id傳遞到http header中
for key, value := range carrier {
req.Header.Add(key, value[0])
}
// 發送請求
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
//結束當前請求的span
clientSpan.Finish()
服務器端要拿到客戶端傳遞過來的trace-id,然后創建一個基於該trace-id的子span
也就是認客戶端為父
// 從http頭中提取trace-id
// refer to https://github.com/opentracing/opentracing-go
// refer to https://opentracing.io/docs/overview/inject-extract/
carrier := opentracing.HTTPHeadersCarrier{}
carrier.Set("uber-trace-id", c.GetHeader("uber-trace-id"))
tracer := opentracing.GlobalTracer()
wireContext, err := tracer.Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(c.Request.Header))
if err != nil {
log.Fatal(err)
}
// 由傳遞過來的trace-id作為父span
serverSpan := opentracing.StartSpan(
"server-two-http-root",
ext.RPCServerOption(wireContext))
ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
selfCall(ctx)
c.JSON(200, gin.H{
"message": "server two response",
})
serverSpan.Finish()
消息隊列實現全鏈路

這里以nsq為例
生產者將trace-id封裝到msg中
tracer := opentracing.GlobalTracer()
clientSpan, clientCtx := opentracing.StartSpanFromContext(ctx, "nsq-one-req")
carrier := opentracing.HTTPHeadersCarrier{}
tracer.Inject(clientSpan.Context(), opentracing.HTTPHeaders, carrier)
// 將trace-id封裝到消息中,由消息隊列,傳給消費者
msg, _ := json.Marshal(carrier)
// 生產消息
Produce(TopicName, string(msg))
clientSpan.Finish()
return clientCtx
消費者將trace-id取出,並創建基於該trace-id的子span,相當於認生產者為父
// 消費者消息處理,處理server-one發送過來的消息
func eventHandler(message string) error {
fmt.Printf("event msg:%s\n", message)
jaeger := initJaeger("two1")
defer jaeger.Close()
// 讀取消息中的trace-id
var carrier opentracing.HTTPHeadersCarrier
json.Unmarshal([]byte(message), &carrier)
tracer := opentracing.GlobalTracer()
wireContext, err := tracer.Extract(
opentracing.HTTPHeaders,
carrier)
if err != nil {
log.Fatal(err)
}
// 由傳遞過來的trace-id作為父span
serverSpan := opentracing.StartSpan(
"nsq_two",
ext.RPCServerOption(wireContext))
ctx := opentracing.ContextWithSpan(context.Background(), serverSpan)
selfCall(ctx)
serverSpan.Finish()
return nil
}
