分布式链路追踪系统-Jaeger实践
导语
Jaeger是用Go写的一款软件,刚好我们最近也在转go开发,看到了网上一些推文的介绍,所以就做一波实践,并分享给大家。
什么是Jaeger?
Jaeger的工作原理
Jaeger 内含多个组件,这些组件可以协同工作,一起收集、存储和可视化跨度与迹线。
Jaeger-client 包括含用于分布式跟踪的 OpenTracing API 的特定语言实施。Jaeger客户端代码库,便于不同语言的项目来介入到Jaeger中,当我们的应用程序装载上之后,client会负责收集并发送数据到Agent。 Jaeger-agent 是一个网络守护进程,可侦听通过用户数据报协议发送的跨度。该代理应与所检测的应用放置在同一主机上。这通常是通过 Kubernetes 等容器环境中的 sidecar 实现的。 Jaeger-collector 从agent收集traces信息,并通过处理管道处理他们,再写入后端存储(backends)。当前的collector工作主要是管理trace,建立索引,执行相关转换,并最终存储它们。 Jaeger 控制台是用于直观查看分布式跟踪数据的用户界面。Query查询是一种从存储中检索trace,并提供UI以显示它们的服务。上图中就展示了一次Trace的数据流向,作为一次系统作用的数据传播/执行图,即可以在Jaeger UI上展示出来
表示一个逻辑工作单元,包含有操作名称、起始时间以及操作耗时。span之间可以存在嵌套和并排关系,span之间也有顺序。
安装实践
1、使用docker安装是最简单的,只需要一行指令就能完成安装
git clone [email protected]:jaegertracing/jaeger.git jaeger cd jaeger/examples/hotrod go run ./main.go alldocker run -d --name=jaeger -p6831:6831/udp -p16686:16686 jaegertracing/all-in-one:latest
这个应用是Jaeger仓库中的一个示例,我们clone下来并启动
git clone [email protected]:jaegertracing/jaeger.git jaeger cd jaeger/examples/hotrod go run ./main.go allgit clone [email protected]:jaegertracing/jaeger.git jaegercd jaeger/examples/hotrodgo run ./main.go all
4、hotrod启动后访问webUI:http://127.0.0.1:8080 ,这是官方提供的示例客户端,我们可以通过它来进行实践
可以看到左上角有个客户端id,每次刷新都会生成一个新的;而下面的四个按钮分别代表四个客户,点击一个按钮就会给他下单一辆车并送过去,就是一个下单请求,响应数据是车牌号以及预计到达时间。
5、点击按钮下单
6、回到Jaeper Web UI,查看Traces
请求到达路由服务,路由服务根据路径调用handler
handler内部调用service服务
service根据路由调用其service-handler
service-handler内调用mysql
span-id
操作名
开始时间
结束时间
耗时
tag: 用户自定义标签便于查询过滤和理解数据
log: 记录 span 内特定时间或事件的日志信息,以及应用程序本身的其他调试或信息输出
span context: 跨越进程边界,传递到子级 span 的状,常在追踪示意图中创建上下文时使用
我们再简单用Go来写一个简单的Demo:
func initJaeger(service string) (opentracing.Tracer, io.Closer) {cfg := &config.Configuration{Sampler:&config.SamplerConfig{Type: "const",Param:1,},Reporter: &config.ReporterConfig{LogSpans: true,LocalAgentHostPort: "127.0.0.1:6831",},}tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger))if err != nil {panic(fmt.Sprintf("Error: connot init Jaeger: %v\n", err))}return tracer, closer}
2、然后我们在main函数中创建调用InitJaeger,并创建一个root span,调用两个函数,分别表示调用两个分布式服务。我们用ContextWithSpan来创建一个新的ctx,将span的信息与context关联,传到TestDemo中时,需要创建一个子span,父span是ctx中的span。
func TestDemo(req string, ctx context.Context) (reply string) {// 1. 创建spanspan, _ := opentracing.StartSpanFromContext(ctx, "span_testdemo")defer func() {// 4. 接口调用完,在tag中设置request和replyspan.SetTag("request", req)span.SetTag("reply", reply)span.Finish()}()println(req)//2. 模拟耗时time.Sleep(time.Second/2)//3. 返回replyreply = "TestDemoReply"return}// TestDemo2, 和上面TestDemo 逻辑代码一样func TestDemo2(req string, ctx context.Context) (reply string) {span, _ := opentracing.StartSpanFromContext(ctx, "span_testdemo2")defer func() {span.SetTag("request", req)span.SetTag("reply", reply)span.Finish()}()println(req)time.Sleep(time.Second/2)reply = "TestDemo2Reply"return}func main() {tracer, closer := initJaeger("jager-test-demo")defer closer.Close()opentracing.SetGlobalTracer(tracer)span := tracer.StartSpan("span_root")ctx := opentracing.ContextWithSpan(context.Background(), span)r1 := TestDemo("Hello TestDemo", ctx)r2 := TestDemo2("Hello TestDemo2", ctx)fmt.Println(r1, r2)span.Finish()}
运行demo会打印出来:
21:57:30 debug logging disabled
21:57:30 Initializing logging reporter
21:57:30 debug logging disabled
Hello TestDemo
21:57:30 Reporting span 2163520004cced2a:4155a263b5147904:2163520004cced2a:1
Hello TestDemo2
21:57:31 Reporting span 2163520004cced2a:01928bf482621c17:2163520004cced2a:1
TestDemoReply TestDemo2Reply
21:57:31 Reporting span 2163520004cced2a:2163520004cced2a:0000000000000000:1
然后在去web UI上刷新查看,就会出现trace记录
总结
根据上面的实践,我们就能方便定位到每一个trace,甚至每一个span出现问题的位置,提高我们的开发效率。当然jaeger还有很多其他的功能,例如接口出现问题的时候,对比2次相同的请求其trace的不同之处,进而缩小范围找到具体不同的span出现在哪。还有更多就不一一列举了,感兴趣的小伙伴可以实践一下,一起探讨。