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
}
|
请发表评论