1. 程式人生 > 實用技巧 >微服務:分散式鏈路追蹤系統-jaeger

微服務:分散式鏈路追蹤系統-jaeger

簡介

jaeger是一個比較有名的分散式鏈路追蹤系統,底層用golang實現,相容opentracing標準。

部署

我們用docker部署,整合整套環境,docker地址: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:latest

執行完成後,用
命令:docker ps
看看執行起來沒,這裡看結果已經運行了:

訪問jaeger的web介面:
localhost:16686
如果你是遠端,這裡的localhost可以換成你的伺服器ip,或者你配置的域名。

簡單demo

先編寫一個初始化jaeger tracer的initJaeger方法:
此時我們要在reporter中配置jaeger Agent的ip與埠,以便將tracer的資訊釋出到agent中。配置LocalAgentHostPort引數為127.0.0.1:6381,6381介面是接受壓縮格式的thrift協議資料。如果是遠端這裡的 127.0.0.1 可以設定為你遠端ip地址。
取樣率暫且設定為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: connot init Jaeger: %v\n", err))
    }
    return tracer, closer
}

然後我們在main函式中建立呼叫InitJaeger,並建立一個root span,呼叫兩個函式,分別表示呼叫兩個分散式服務。

我們用ContextWithSpan來建立一個新的ctx,將span的資訊與context關聯,傳到TestDemo中時,需要建立一個子span,父span是ctx中的span。

我們在TestDemo中呼叫StartSpanFromContext時,忽略了第二個引數,這是利用子span建立的新的context,當我們在TestDemo中再呼叫別的比如TestDemo2時,我們應該使用新的context,而不是傳入的ctx。

注意StartSpanFromContext會用到opentracing.SetGlobalTracer()來啟動新的span,所以在main函式中需要呼叫。

func TestDemo(req string, ctx context.Context) (reply string) {
    // 1. 建立span
    span, _ := opentracing.StartSpanFromContext(ctx, "span_testdemo")
    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 = "TestDemoReply"
    return
}

// TestDemo2, 和上面TestDemo 邏輯程式碼一樣
func TestDemo2(req string, ctx context.Context) (reply string) {
    span, _ := opentracing.StartSpanFromContext(ctx, "span_testdemo2")
    defer func() {
        span.SetTag("request", req)
        span.SetTag("reply", reply)
        span.Finish()
    }()

    println(req)
    time.Sleep(time.Second/2)
    reply = "TestDemo2Reply"
    return
}

func main() {
    tracer, closer := initJaeger("jager-test-demo")
    defer closer.Close()
    opentracing.SetGlobalTracer(tracer)

    span := tracer.StartSpan("span_root")
    ctx := opentracing.ContextWithSpan(context.Background(), span)
    r1 := TestDemo("Hello TestDemo", ctx)
    r2 := TestDemo2("Hello TestDemo2", ctx)
    fmt.Println(r1, r2)
    span.Finish()
}

執行demo:
go run simple/main.go
執行提交的span資訊會打印出來:

21:57:30 debug logging disabled
21:57:30 Initializing logging reporter
21:57:30 debug logging disabled
Hello TestDemo
21:57:30 Reporting span 2163520004cced2a:4155a263b5147904:2163520004cced2a:1
Hello TestDemo2
21:57:31 Reporting span 2163520004cced2a:01928bf482621c17:2163520004cced2a:1
TestDemoReply TestDemo2Reply
21:57:31 Reporting span 2163520004cced2a:2163520004cced2a:0000000000000000:1

然後在去jaeger UI上重新整理檢視,會出現記錄:
可以發現有分層,時間耗時也明顯,介面先後呼叫也很清晰。

點選上面的 jager-test-demo 進去,可以看到下面的這種呼叫情況:

參考