开源项目

手把手教媳妇jaeger简单场景

2020-12-21  本文已影响0人  牛空空

分布式服务中,通过日志查看错误以及追踪问题,是一件非常痛苦的事情,由此我们可以使用链路追踪来快速定位问题,查看服务调用请用情况,埋点做一些必要的参数标记,此处使用uber开源的jaeger,它完美实现了openTraceing 标准,支持不同语言的客户端,其它的不再赘述了,网上很多的先关学习文档,这里只给出简单的使用场景,为了快速展示应用,直接用docker启动一个服务,jaegertracing/all-in-one,自行参考官网文档


1608554929(1).jpg

打开jaeger-ui的界面如下


1608555027(1).jpg
package main

import (
    "context"
    "fmt"
    "github.com/opentracing/opentracing-go"
    "github.com/uber/jaeger-client-go"
    "github.com/uber/jaeger-client-go/config"
    "io"
    "log"
    "strings"
)


//初始化tracer
func InitTracer() (opentracing.Tracer,io.Closer){
    cfg := &config.Configuration{
        ServiceName: "zzy_jaeger_test",
        Sampler: &config.SamplerConfig{
            Type:                     jaeger.SamplerTypeConst,//固定采样
            Param:                    1,//1全采样,0不采样
        },
        Reporter: &config.ReporterConfig{
            LogSpans:                   true,
            LocalAgentHostPort:         "192.168.1.246:6831",
        },

    }

    //创建tracer
    tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
    //设置全局tracer
    opentracing.SetGlobalTracer(tracer)
    if err != nil {
        log.Fatal(err)
    }
    return tracer,closer
}

func main() {

    trace, closer := InitTracer()

    defer closer.Close()
    // 创建父span
    spanRoot := trace.StartSpan("rootSpan 埋点")
    // 在函数返回的时候调用finish结束这个span
    defer spanRoot.Finish()
    spanRoot.SetTag("gender","男")
    spanRoot.SetBaggageItem("foo","todo")
    //#创建上下文,使用上下文来传递span
    ctx := opentracing.ContextWithSpan(context.Background(), spanRoot)

    ct := spanRoot.Context().(jaeger.SpanContext)

    fmt.Println("traceID:",ct.TraceID().String())

    // 创建一个childspan
    //childspan:=trace.StartSpan("childSpan 埋点",opentracing.ChildOf(spanRoot.Context()))
    //defer childspan.Finish()
    //# 下面为你的业务代码
    //videoURL := c.DefaultPostForm("url", "")
    //将ctx上下文传到调用的函数里
    ExtractVideo(strings.TrimSpace("媳妇"), ctx)

}

func ExtractVideo(videoURL string, ctx context.Context)  {
    //创建子span
    span, _ := opentracing.StartSpanFromContext(ctx, "childSpan 埋点")
    defer func() {
        span.SetTag("媳妇", videoURL)
        span.Finish()
    }()

}

直接运行,然后在jaeger-ui上查看service,选择最下面的trace,就可以看到刚才的追踪,以及经过的span,点击这个追踪的详情,可以看得到所有设置的tags的值
上边的是最基础简单的例子,好了,下面看看在web服务应用中的使用技巧了

package main

import (
    "fmt"
    "github.com/opentracing/opentracing-go"
    "github.com/uber/jaeger-client-go"
    "github.com/uber/jaeger-client-go/config"
    "io"
    "log"
    "net/http"
)


func NewTrace() (opentracing.Tracer,io.Closer) {

    conf :=  &config.Configuration{
        ServiceName: "trace_srv",
        Sampler: &config.SamplerConfig{
            Type:  jaeger.SamplerTypeConst, //固定采样
            Param: 1,                       //1全采样,0不采样
        },
        Reporter: &config.ReporterConfig{
            LogSpans:           true,
            LocalAgentHostPort: "192.168.1.246:6831",
        },
    }
    trace, closer, err := conf.NewTracer()
    if err != nil {
        log.Fatal(err)
    }
    //设置全局trace,一个微服务对应一个trace的service
    opentracing.SetGlobalTracer(trace)
    return trace, closer
}


//中间件
func tracingMW(next http.Handler) http.HandlerFunc {

    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        tracer, closer := NewTrace()
        defer closer.Close()
        //以请求的url为operatorName
        rootSpan := tracer.StartSpan(r.URL.Path)
        defer rootSpan.Finish()
        //随便记录一些参数
        rootSpan.SetTag("Method",r.Method)
        rootSpan.SetTag("url",r.URL.Path)
        //打印traceID
        sctx := rootSpan.Context().(jaeger.SpanContext)
        fmt.Println("traceID:",sctx.TraceID().String())
        //利用context传递span在下一个请求中获取chidspan
        ctx := opentracing.ContextWithSpan(r.Context(), rootSpan)
        next.ServeHTTP(w,r.WithContext(ctx))

    })
}

func test(w http.ResponseWriter, r *http.Request)  {
    //从context中拿childspan
    span, _ := opentracing.StartSpanFromContext(r.Context(), "test")
    defer span.Finish()
    ctx := span.Context().(jaeger.SpanContext)
    fmt.Println("traceID:",ctx.TraceID().String())
    span.SetTag("todo","foo")
    fmt.Fprint(w,"haha")

}

func main()  {
    http.HandleFunc("/",tracingMW(http.HandlerFunc(test)))
    http.ListenAndServe(":8080",nil)

}

在web请求中设置一个中间件,在中间件设置rootSpan,然后利用context获取子span,在后续的业务处理中可以一次生成子span,并且为子span名命名
只演示大概的例子,每次中间件都要生成trace,将trace注入容器中保证唯一性,或者直接生成全局唯一的trace实例,根据需要自行扩展

下面演示跨服务(进程)之间实现追踪埋点,先看用http协议跨服务的调用追踪方式,直接在test方法中修改
func test(w http.ResponseWriter, r *http.Request)  {

    url := "http://localhost:8000"
    client := &http.Client{}
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        log.Println(err)
    }

    span, _ := opentracing.StartSpanFromContext(r.Context(), "remote request svc2")
    defer span.Finish()
    span.SetTag("role", "admin")
    span.SetBaggageItem("name", "媳妇")

    ext.SpanKindRPCClient.Set(span)
    ext.HTTPUrl.Set(span, url)
    ext.HTTPMethod.Set(span, "GET")
    span.Tracer().Inject(
        span.Context(),
        opentracing.HTTPHeaders,
        opentracing.HTTPHeadersCarrier(req.Header),
    )

    resp, err := client.Do(req)
    if err != nil {
        log.Println("请求错误:",err)
    }

    log.Println(resp.Status)
    bytes, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Fprint(w,err.Error())
    }

    fmt.Fprint(w,string(bytes))

}

接下来是目标服务的代码

package main

import (
    "fmt"
    "github.com/uber/jaeger-client-go"
    "net/http"
    "log"
    opentracing "github.com/opentracing/opentracing-go"
    "github.com/opentracing/opentracing-go/ext"
    otlog "github.com/opentracing/opentracing-go/log"
    "github.com/uber/jaeger-client-go/config"
)

func fromEnv() (*config.Configuration,error) {

    return  &config.Configuration{
        ServiceName: "svc2",
        Sampler: &config.SamplerConfig{
            Type:  jaeger.SamplerTypeConst, //固定采样
            Param: 1,                       //1全采样,0不采样
        },
        Reporter: &config.ReporterConfig{
            LogSpans:           true,
            LocalAgentHostPort: "192.168.1.246:6831",
        },
    },nil
}

func main(){
    http.HandleFunc("/",test)
    http.ListenAndServe(":8000",nil)
}

func test(w http.ResponseWriter,r *http.Request){
    log.Println(r.Header,r.URL)

    //cfg,err:=config.FromEnv()
    cfg,err:=fromEnv()
    if err!=nil {
        log.Println(err)
    }
    tracer,closer,err:=cfg.NewTracer()
    if err!=nil {
        log.Println(err)
    }
    defer closer.Close()

    spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
    span := tracer.StartSpan("get haha", ext.RPCServerOption(spanCtx))
    defer span.Finish()
    ctx := spanCtx.(jaeger.SpanContext)
    log.Println("traceID:",ctx.TraceID().String())
    log.Println(span.BaggageItem("name"))

    span.LogFields(
        otlog.String("event", "string-format"),
        otlog.String("value", "hello wrold"),
    )
    fmt.Fprint(w,"呵呵,进程的追踪结束")
}

打开浏览器,输入http://localhost:8080/put请求,可以在jaeger-ui中查看请求经过过的span,以及在控制台,可以发现请求的traceID保持一致

1608561179(1).jpg
追踪链路上经过的两个service,继续切换到系统的体系结构图中查看DAG图可以看到服务的调用结构如下:
1608561352(1).jpg
下面演示在grpc的场景下,追踪的方式,和http跨进程调用差不多,直接附上代码
太晚了,明天继续。。。
上一篇 下一篇

猜你喜欢

热点阅读