参考地址:https://github.com/yurishkuro/opentracing-tutorial/tree/master/go
jaeger是一个比较有名的分布式链路追踪系统,底层用golang实现,兼容opentracing标准,这里利用其go-client来实现一个最简单的demo,仅供参考。
1. 安装必要的包:
"github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" "github.com/uber/jaeger-client-go/config"
2. 安装部署jaeger整套:
这里利用jaeger提供的docker,集成了整套环境,利用内存存储:docker hub地址:https://hub.docker.com/r/jaegertracing/all-in-one
直接运行一下命令启动docker:
docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \ -p 5775:5775/udp \ -p 6831:6831/udp \ -p 6832:6832/udp \ -p 5778:5778 \ -p 16686:16686 \ -p 14268:14268 \ -p 9411:9411 \ jaegertracing/all-in-one:1.9
运行ok后我们docker ps看一下是否正常运行:
现在可以访问localhost:16686来查看jaeger的UI界面:
3. 编写demo
先编写一个初始化jaeger tracer的initJaeger方法:
此时我们要在reporter中配置jaeger Agent的ip与端口,以便将tracer的信息发布到agent中。
配置LocalAgentHostPort参数为127.0.0.1:6381,6381接口是接受压缩格式的thrift协议数据。
采样率暂且设置为1。
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: cannot init Jaeger: %v\n", err)) } return tracer, closer }
然后我们在main函数中创建调用InitJaeger,并创建一个root span,调用两个函数,分别表示调用两个分布式服务。
我们用ContextWithSpan来创建一个新的ctx,将span的信息与context关联,传到foo3中时,需要创建一个子span,父span是ctx中的span。
我们在foo3中调用StartSpanFromContext时,忽略了第二个参数,这是利用子span创建的新的context,当我们在foo3中再调用别的比如foo5时,我们应该使用新的context,而不是传入的ctx。
注意StartSpanFromContext会用到opentracing.SetGlobalTracer()来启动新的span,所以在main函数中需要调用。
func foo3(req string, ctx context.Context) (reply string){ //1.创建子span span, _ := opentracing.StartSpanFromContext(ctx, "span_foo3") defer func() { //4.接口调用完,在tag中设置request和reply span.SetTag("request", req) span.SetTag("reply", reply) span.Finish() }() println(req) //2.模拟处理耗时 time.Sleep(time.Second/2) //3.返回reply reply = "foo3Reply" return } //跟foo3一样逻辑 func foo4(req string, ctx context.Context) (reply string){ span, _ := opentracing.StartSpanFromContext(ctx, "span_foo4") defer func() { span.SetTag("request", req) span.SetTag("reply", reply) span.Finish() }() println(req) time.Sleep(time.Second/2) reply = "foo4Reply" return } func main() { tracer, closer := initJaeger("jaeger-demo") defer closer.Close() opentracing.SetGlobalTracer(tracer)//StartspanFromContext创建新span时会用到 span := tracer.StartSpan("span_root") ctx := opentracing.ContextWithSpan(context.Background(), span) r1 := foo3("Hello foo3", ctx) r2 := foo4("Hello foo4", ctx) fmt.Println(r1, r2) span.Finish() }
4. 运行结果:
运行完提交的span信息会被log打印出来:
2019/02/26 13:21:44 Initializing logging reporter Hello foo3 2019/02/26 13:21:45 Reporting span 8acae1d479b9829:12f2820e87c75e51:8acae1d479b9829:1 Hello foo4 2019/02/26 13:21:45 Reporting span 8acae1d479b9829:7cca0d1cc894735:8acae1d479b9829:1 foo3Reply foo4Reply 2019/02/26 13:21:45 Reporting span 8acae1d479b9829:8acae1d479b9829:0:1 Process finished with exit code 0
同时jaeger UI上会发现对应的记录:
可以发现有很明显的分层,时间耗时也很明显,接口先后调用也很清晰。
NEXT~~~~~
进阶版:我们现在只是在同一个服务中实现了jaeger的demo,如果在分布式的服务中,如何实现jaeger呢?