在本章节中,我们演示两个示例,一个用于演示 baggage
服务间上下文数据传递;一个用于演示较完整的 HTTP+DB+Redis+Logging
组件的链路跟踪。
baggage
服务间链路数据传递
示例代码地址: https://github.com/gogf/gf-tracing/tree/master/examples/http
客户端
package main
import (
"context"
"gftracing/tracing"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/net/gtrace"
)
const (
ServiceName = "tracing-http-client"
JaegerUdpEndpoint = "localhost:6831"
)
func main() {
flush, err := tracing.InitJaeger(ServiceName, JaegerUdpEndpoint)
if err != nil {
g.Log().Fatal(err)
}
defer flush()
StartRequests()
}
func StartRequests() {
ctx, span := gtrace.NewSpan(context.Background(), "StartRequests")
defer span.End()
ctx = gtrace.SetBaggageValue(ctx, "name", "john")
client := g.Client().Use(ghttp.MiddlewareClientTracing)
content := client.Ctx(ctx).GetContent("http://127.0.0.1:8199/hello")
g.Log().Ctx(ctx).Print(content)
}
客户端代码简要说明:
- 首先,客户端也是需要通过
initTracer
方法初始化Jaeger
。 - 随后,这里通过
gtrace.SetBaggageValue(ctx, "name", "john")
方法设置了一个baggage
,该baggage
将会在该请求的所有链路中传递。不过我们该示例也有两个节点,因此该baggage
数据只会传递到服务端。该方法会返回一个新的context.Context
上下文变量,在随后的调用链中我们将需要传递这个新的上下文变量。 - 其中,这里通过
g.Client().Use(ghttp.MiddlewareClientTracing)
创建一个HTTP客 户端请求对象,并通过Use
方法设置客户端请求的拦截器。随后该客户端对象所有的的请求都将会通过拦截器处理后再发出。这里的注册拦截器ghttp.MiddlewareClientTracing
主要用于启用链路跟踪特性,否则客户端请求中不会自动增加链路信息。 - 最后,这里使用了
g.Log().Ctx(ctx).Print(content)
方法打印服务端的返回内容,其中的Ctx(ctx)
便是将链路信息传递给日志组件,如果ctx
上下文对象中存在链路信息时,日志组件会同时自动将TraceId
输出到日志内容中。
服务端
package main
import (
"gftracing/tracing"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/net/gtrace"
)
const (
ServiceName = "tracing-http-server"
JaegerUdpEndpoint = "localhost:6831"
)
func main() {
flush, err := tracing.InitJaeger(ServiceName, JaegerUdpEndpoint)
if err != nil {
g.Log().Fatal(err)
}
defer flush()
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(ghttp.MiddlewareServerTracing)
group.GET("/hello", HelloHandler)
})
s.SetPort(8199)
s.Run()
}
func HelloHandler(r *ghttp.Request) {
ctx, span := gtrace.NewSpan(r.Context(), "HelloHandler")
defer span.End()
value := gtrace.GetBaggageVar(ctx, "name").String()
r.Response.Write("hello:", value)
}
服务端代码简要说明:
- 当然,服务端也是需要通过
initTracer
方法初始化Jaeger
。 - 服务端通过
group.Middleware(ghttp.MiddlewareServerTracing)
注册一个分组路由中间件,该中间件的作用是启用链路跟踪特性,所有该分组路由下的请求都将会经过中间件的处理后再将请求转交给路由方法。我们在项目中也可以注册 全局中间件 的形式来启用链路跟踪特性,关于中间件的介绍请查看 路由管理-中间件/拦截器 章节。 - 服务端通过
gtrace.GetBaggageVar(ctx, "name").String()
方法获取客户端提交的baggage
信息,并转换为字符串返回。
效果查看
启动服务端:
启动客户端:
可以看到,客户端提交的 baggage
已经被服务端成功接收到并打印返回。并且客户端在输出日志内容的时候也同时输出的 TraceId
信息。 TraceId
是一条链路的唯一ID,可以通过该ID检索该链路的所有日志信息,并且也可以通过该 TraceId
在 Jaeger
系统上查询该调用链路详情。
在 Jaeger
上查看链路信息:
可以看到在这里出现了两个服务名称: tracing-http-client
和 tracing-http-server
,表示我们这次请求涉及到两个服务,分别是HTTP请求的客户端和服务端,并且每个服务中分别涉及到 2
个 span
链路节点。
我们点击这个 trace
的详情,可以看得到调用链的层级关系。并且可以看得到客户端请求的地址、服务端接收的路由以及服务端路由函数名称。我们这里来介绍一下客户端的 Atttributes
信息和 Events
信息,也就是 Jaeger
中展示的 Tags
信息和 Process
信息。
HTTP Client Attributes
Attribute/Tag | 说明 |
---|---|
otel.instrumentation_library.name | 当前仪表器名称,往往是当前 span 操作的组件名称 |
otel.instrumentation_library.version | 当前仪表器组件版本 |
span.kind | 当前 span 的类型,一般由组件自动写入。常见 span 类型为: |
类型 | 说明 |
---|---|
client | 客户端 |
server | 服务端 |
producer | 生产者,常用于MQ |
consumer | 消费者,常用于MQ |
internal | 内部方法,一般业务使用 |
undefined |