opentracing: jaeger的简单实现
参考地址:https://github.com/yurishkuro/opentracing-tutorial/tree/master/go
jaeger是一个比较有名的分布式链路追踪系统,底层用golang实现,兼容opentracing标准,这里利用其go-client来实现一个最简单的demo,仅供参考。
1. 安装必要的包:
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"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
现在可以访问localhost:16686来查看jaeger的UI界面:

3. 编写demo
先编写一个初始化jaeger tracer的initJaeger方法:
此时我们要在reporter中配置jaeger Agent的ip与端口,以便将tracer的信息发布到agent中。
配置接口是接受压缩格式的thrift协议数据。
采样率暂且设置为1
然后我们在main函数中创建调用InitJaeger,并创建一个root span,调用两个函数,分别表示调用两个分布式服务。
我们用ContextWithSpan来创建一个新的ctx,将span的信息与context关联,传到formatstring中时,需要创建一个子span,父span是ctx中的span。
我们在formatstring中调用StartSpanFromContext时,忽略了第二个参数,这是利用子span创建的新的context,当我们在formatstring中再调用别的比如服务时,我们应该使用新的context,而不是传入的ctx。
注意StartSpanFromContext会用到opentracing.SetGlobalTracer()来启动新的span,所以在main函数中需要调用。
代码:
func main() {
if len(os.Args) != 2{
panic("ERROR: Expecting one argument")
}
tracer, cloer := Init("hello-world")
defer cloer.Close()
opentracing.SetGlobalTracer(tracer)
helloTo := os.Args[1]
span := tracer.StartSpan("say-hello")
span.SetTag("hello-to", helloTo)
defer span.Finish()
ctx := opentracing.ContextWithSpan(context.Background(), span)
helloStr := formatString(ctx, helloTo)
printHello(ctx, helloStr)
//helloStr := fmt.Sprintf("Hello, %s!", helloTo)
}
func formatString(ctx context.Context, helloTo string) string {
span, _ := opentracing.StartSpanFromContext(ctx, "formatString")
defer span.Finish()
v := url.Values{}
v.Set("helloTo", helloTo)
url := "http://localhost:8081/format?" + v.Encode()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err.Error())
}
xhttp := &http.Client{}
resp, err := xhttp.Do(req)
if err != nil {
panic(err.Error())
}
defer resp.Body.Close()
buf, err := ioutil.ReadAll(resp.Body)
helloStr := string(buf)
span.LogFields(
log.String("event", "string-format"),
log.String("value", helloStr),
)
return helloStr
}
func printHello(ctx context.Context, helloStr string) {
span, _ := opentracing.StartSpanFromContext(ctx, "printHello")
defer span.Finish()
v := url.Values{}
v.Set("helloStr", helloStr)
url := "http://localhost:8082/publish?" + v.Encode()
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err.Error())
}
xhttp := &http.Client{}
_, err = xhttp.Do(req)
if err != nil {
panic(err.Error())
}
}
// Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout.
func Init(service string) (opentracing.Tracer, io.Closer) {
cfg := &config.Configuration{
Sampler: &config.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &config.ReporterConfig{
LogSpans: true,
},
}
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
}
运行结果:
192:client small_lei_it$ go run main.go xiaolei
2020/03/04 10:18:55 Initializing logging reporter
2020/03/04 10:18:55 Reporting span 7329f9cac13f0f0f:4398534377caaa9a:7329f9cac13f0f0f:1
2020/03/04 10:18:55 Reporting span 7329f9cac13f0f0f:6eaefac21f1e1179:7329f9cac13f0f0f:1
2020/03/04 10:18:55 Reporting span 7329f9cac13f0f0f:7329f9cac13f0f0f:0:1
同时jaeger UI上会发现对应的记录:
可以发现有很明显的分层,时间耗时也很明显,接口先后调用也很清晰

调用树结构图:


浙公网安备 33010602011771号