使用opentracing,jaeger实现golang链路追踪

麦穗儿 · · 2340 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Jaeger 是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API。
我们可以使用opentracing在关键代码中进行埋点记录,线上可以快速直观查看调用执行情况。

要点说明

config设定
cfg := &config.Configuration{
    Sampler: &config.SamplerConfig{
        Type:  samplerType,
        Param: samplerParam,
    },
    Reporter: &config.ReporterConfig{
        LogSpans: true,
    },
}

其中关于SamplerConfig的Type可以选择

  • const,全量采集。param采样率设置0,1 分别对应打开和关闭
  • probabilistic ,概率采集。param默认万份之一,0~1之间取值,
  • rateLimiting ,限速采集。param每秒采样的个数
  • remote 动态采集策略。param值于probabilistic的参数一样。在收到实际值之前的初始采样率。改值可以通过环境变量的JAEGER_SAMPLER_PARAM设定
生成jaeger tracer
func (c Configuration) NewTracer(options ...Option) (opentracing.Tracer, io.Closer, error)
设置为全局的单例tracer
func SetGlobalTracer(tracer Tracer)
生成开始一个Span
StartSpan(operationName string, opts ...StartSpanOption) Span
返回span的SpanContext的reference
func ContextWithSpan(ctx context.Context, span Span) context.Context
生成子Span
func StartSpanFromContext(ctx context.Context, operationName string, opts ...StartSpanOption) (Span, context.Context)
记录关于Span相关的key:value数据
LogFields(fields ...log.Field)

到此如果只需要追踪在同一process的链路就已经可以了。如果希望能够追踪不同进程中的链路例如,客户端通过http请求服务端,服务端回应整个链路的追踪需要用到以下的处理。

使用Inject和Extract通过RPC calls传递span context
Client端
  • 添加import

    import (
        "github.com/opentracing/opentracing-go/ext"
    )
  • 添加Inject

    ext.SpanKindRPCClient.Set(reqSpan)
    ext.HTTPUrl.Set(reqSpan, reqURL)
    ext.HTTPMethod.Set(reqSpan, "GET")
    span.Tracer().Inject(
        span.Context(),
        opentracing.HTTPHeaders,
        opentracing.HTTPHeadersCarrier(req.Header),
    )
Server端
  • 添加import

    import (
        opentracing "github.com/opentracing/opentracing-go"
        "github.com/opentracing/opentracing-go/ext"
        otlog "github.com/opentracing/opentracing-go/log"
        "github.com/yurishkuro/opentracing-tutorial/go/lib/tracing"
    )
  • 从request抽取出span context

    spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
  • 通过饮用从Client端传来的span context生成新的child span

    span := tracer.StartSpan("format", ext.RPCServerOption(spanCtx))
    defer span.Finish()

到此位置基本的点都已经描述了,接下来实际使用

安装所需库

  • 安装支持go的jaeger client library

    go get -u github.com/uber/jaeger-client-go/
    cd $GOPATH/src/github.com/uber/jaeger-client-go/
    git submodule update --init --recursive
    make install

    如果没有安装dep的要提前安装
    在mac下

    brew install dep
  • 安装opentracing

    go get -u github.com/opentracing/opentracing-go

启动jaeger

docker run \
-p 5775:5775/udp \
-p 16686:16686 \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 14268:14268 \
jaegertracing/all-in-one:latest

具体代码

client.go
package main

import (
    "context"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strconv"

    traceconfig "binTest/jaegerTest/CSJaeger/tracelib"

    "github.com/opentracing/opentracing-go"
    "github.com/opentracing/opentracing-go/ext"
    "github.com/opentracing/opentracing-go/log"
)

const (
    URL        = "http://localhost:8080"
    LIST_API   = "/getList"
    RESULT_API = "/getResult"
)

var (
    flag = make(chan bool)
)

func saveResponse(response []byte) error {
    err := ioutil.WriteFile("response.txt", response, 0644)
    if err != nil {
        fmt.Println(err)
        return err
    }

    return nil
}

func sendRequest(req *http.Request, ctx context.Context) {
    reqPrepareSpan, _ := opentracing.StartSpanFromContext(ctx, "Client_sendRequest")
    defer reqPrepareSpan.Finish()

    go func(req *http.Request) {
        resp, err := http.DefaultClient.Do(req)

        if err != nil {
            fmt.Printf("Do send requst failed(%s)\n", err)
            return
        }

        respSpan, _ := opentracing.StartSpanFromContext(ctx, "Client_response")
        defer respSpan.Finish()

        defer resp.Body.Close()

        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            fmt.Printf("ReadAll error(%s)\n", err)
            return
        }

        if resp.StatusCode != 200 {
            return
        }

        fmt.Printf("Response:%s\n", string(body))

        respSpan.LogFields(
            log.String("event", "getResponse"),
            log.String("value", string(body)),
        )

        saveResponse(body)

        flag <- true
    }(req)
}

func main() {

    if len(os.Args) < 2 {
        fmt.Println("Argument error(getlist or getresult number) ")
        os.Exit(1)
    }

    tracer, closer := traceconfig.TraceInit("CS-tracing", "const", 1)
    defer closer.Close()
    opentracing.SetGlobalTracer(tracer)

    span := tracer.StartSpan(fmt.Sprintf("%s trace", os.Args[1]))
    span.SetTag("trace to", os.Args[1])
    defer span.Finish()
    ctx := opentracing.ContextWithSpan(context.Background(), span)

    api := ""
    var err error

    if os.Args[1] == "getlist" {
        api = LIST_API
    } else if os.Args[1] == "getresult" {
        api = RESULT_API
        num, err := strconv.Atoi(os.Args[2])

        if err != nil || num <= 0 {
            fmt.Println("getresult input parameter error!")
            os.Exit(1)
        }
    }

    reqSpan, _ := opentracing.StartSpanFromContext(ctx, "Client_"+api+" request")
    defer reqSpan.Finish()

    reqURL := URL + api
    req, err := http.NewRequest("GET", reqURL, nil)

    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

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

    if os.Args[1] == "getresult" {
        q := req.URL.Query()
        q.Add("num", os.Args[2])
        req.URL.RawQuery = q.Encode()
    }

    fmt.Println(req.URL.String())
    reqSpan.LogFields(
        log.String("event", api),
        log.String("value", api),
    )

    sendRequest(req, ctx)

    <-flag
}
server.go
package main

import (
    "fmt"
    "io"
    "net/http"
    "strconv"

    traceconfig "binTest/jaegerTest/CSJaeger/tracelib"

    "github.com/opentracing/opentracing-go"
    "github.com/opentracing/opentracing-go/ext"
)

var (
    tracer opentracing.Tracer
)

func GetListProc(w http.ResponseWriter, req *http.Request) {

    spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
    span := tracer.StartSpan("GetListProc", ext.RPCServerOption(spanCtx))
    defer span.Finish()

    fmt.Println("Get request getList")
    respList := []string{"l1", "l2", "l3", "l4", "l5"}
    respString := ""

    for _, v := range respList {
        respString += v + ","
    }

    fmt.Println(respString)
    io.WriteString(w, respString)
}

func GetResultProc(w http.ResponseWriter, req *http.Request) {

    spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
    span := tracer.StartSpan("GetResultProc", ext.RPCServerOption(spanCtx))
    defer span.Finish()

    keys, ok := req.URL.Query()["num"]

    if !ok || len(keys[0]) < 1 {
        fmt.Println("No request parameter 'num' error! ")
        return
    }

    num, err := strconv.Atoi(keys[0])
    if err != nil {
        fmt.Println("num invalidate")
        return
    }

    result := 0

    for i := 0; i < num; i++ {
        result += i
    }

    respString := fmt.Sprintf("Result:%d", result)

    fmt.Println(respString)
    io.WriteString(w, respString)
}

func main() {
    var closer io.Closer
    tracer, closer = traceconfig.TraceInit("Trace-Server", "const", 1)
    defer closer.Close()

    http.HandleFunc("/getList", GetListProc)
    http.HandleFunc("/getResult", GetResultProc)

    http.ListenAndServe(":8080", nil)
}
traceconfig.go
package traceconfig

import (
    "fmt"
    "io"

    "github.com/opentracing/opentracing-go"
    "github.com/uber/jaeger-client-go"
    "github.com/uber/jaeger-client-go/config"
)

func TraceInit(serviceName string, samplerType string, samplerParam float64) (opentracing.Tracer, io.Closer) {
    cfg := &config.Configuration{
        ServiceName: serviceName,
        Sampler: &config.SamplerConfig{
            Type:  samplerType,
            Param: samplerParam,
        },
        Reporter: &config.ReporterConfig{
            LocalAgentHostPort: "127.0.0.1:6831",
            LogSpans:           true,
        },
    }

    tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
    if err != nil {
        panic(fmt.Sprintf("Init failed: %v\n", err))
    }

    return tracer, closer
}

在server目录下执行

go build
./server

在client目录下执行

go build
./client getlist
./client getresult 10000

在浏览器访问http://localhost:16686/

运行效果如下
屏幕快照 2019-12-12 下午8.25.42.png

代码从这里取 https://github.com/BinWang-sh...


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:麦穗儿

查看原文:使用opentracing,jaeger实现golang链路追踪

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2340 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传