golang实现简单的jaeger-demo

参考地址: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呢?

请参考:opentracing: jaeger在grpc中的简单实现