设为首页 加入收藏

TOP

Go微服务全链路跟踪详解(一)
2019-09-23 11:11:32 】 浏览:165
Tags:服务 链路 跟踪 详解

在微服务架构中,调用链是漫长而复杂的,要了解其中的每个环节及其性能,你需要全链路跟踪。 它的原理很简单,你可以在每个请求开始时生成一个唯一的ID,并将其传递到整个调用链。 该ID称为CorrelationID¹,你可以用它来跟踪整个请求并获得各个调用环节的性能指标。简单来说有两个问题需要解决。第一,如何在应用程序内部传递ID; 第二,当你需要调用另一个微服务时,如何通过网络传递ID。

什么是OpenTracing?

现在有许多开源的分布式跟踪库可供选择,其中最受欢迎的库可能是Zipkin²和Jaeger³。 选择哪个是一个令人头疼的问题,因为你现在可以选择最受欢迎的一个,但是如果以后有一个更好的出现呢?OpenTracing?可以帮你解决这个问题。它建立了一套跟踪库的通用接口,这样你的程序只需要调用这些接口而不被具体的跟踪库绑定,将来可以切换到不同的跟踪库而无需更改代码。Zipkin和Jaeger都支持OpenTracing。

如何跟踪服务器端点(server endpoints)?

在下面的程序中我使用“Zipkin”作为跟踪库,用“OpenTracing”作为通用跟踪接口。 跟踪系统中通常有四个组件,下面我用Zipkin作为示例:

  • recorder(记录器):记录跟踪数据

  • Reporter (or collecting agent)(报告器或收集代理):从记录器收集数据并将数据发送到UI程序

  • Tracer:生成跟踪数据

  • UI:负责在图形UI中显示跟踪数据

file

上面是Zipkin的组件图,你可以在Zipkin Architecture中找到它。

有两种不同类型的跟踪,一种是进程内跟踪(in-process),另一种是跨进程跟踪(cross-process)。 我们将首先讨论跨进程跟踪。

客户端程序:

我们将用一个简单的gRPC程序作为示例,它分成客户端和服务器端代码。 我们想跟踪一个完整的服务请求,它从客户端到服务端并从服务端返回。 以下是在客户端创建新跟踪器的代码。它首先创建“HTTP Collector”(the agent)用来收集跟踪数据并将其发送到“Zipkin” UI, “endpointUrl”是“Zipkin” UI的URL。 其次,它创建了一个记录器(recorder)来记录端点上的信息,“hostUrl”是gRPC(客户端)呼叫的URL。第三,它用我们新建的记录器创建了一个新的跟踪器(tracer)。 最后,它为“OpenTracing”设置了“GlobalTracer”,这样你可以在程序中的任何地方访问它。

const (
    endpoint_url = "http://localhost:9411/api/v1/spans"
    host_url = "localhost:5051"
    service_name_cache_client = "cache service client"
    service_name_call_get = "callGet"
)

func newTracer () (opentracing.Tracer, zipkintracer.Collector, error) {
    collector, err := openzipkin.NewHTTPCollector(endpoint_url)
    if err != nil {
        return nil, nil, err
    }
    recorder :=openzipkin.NewRecorder(collector, true, host_url, service_name_cache_client)
    tracer, err := openzipkin.NewTracer(
        recorder,
        openzipkin.ClientServerSameSpan(true))

    if err != nil {
        return nil,nil,err
    }
    opentracing.SetGlobalTracer(tracer)

    return tracer,collector, nil
}

以下是gRPC客户端代码。 它首先调用上面提到的函数“newTrace()”来创建跟踪器,然后,它创建一个包含跟踪器的gRPC调用连接。接下来,它使用新建的gRPC连接创建缓存服务(Cache service)的gRPC客户端。 最后,它通过gRPC客户端来调用缓存服务的“Get”函数。

key:="123"
    tracer, collector, err :=newTracer()
    if err != nil {
        panic(err)
    }
    defer collector.Close()
    connection, err := grpc.Dial(host_url,
        grpc.WithInsecure(), grpc.WithUnaryInterceptor(otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads())),
        )
    if err != nil {
        panic(err)
    }
    defer connection.Close()
    client := pb.NewCacheServiceClient(connection)
    value, err := callGet(key, client)

Trace 和 Span:

在OpenTracing中,一个重要的概念是“trace”,它表示从头到尾的一个请求的调用链,它的标识符是“traceID”。 一个“trace”包含有许多跨度(span),每个跨度捕获调用链内的一个工作单元,并由“spanId”标识。 每个跨度具有一个父跨度,并且一个“trace”的所有跨度形成有向无环图(DAG)。 以下是跨度之间的关系图。 你可以从The OpenTracing Semantic Specification中找到它。

file

以下是函数“callGet”的代码,它调用了gRPC服务端的“Get"函数。 在函数的开头,OpenTracing为这个函数调用开启了一个新的span,整个函数结束后,它也结束了这个span。


const service_name_call_get = "callGet"

func callGet(key string, c pb.CacheServiceClient) ( []byte, error) {
    span := opentracing.StartSpan(service_name_call_get)
    defer span.Finish()
    time.Sleep(5*time.Millisecond)
    // Put root span in context so it will be used in our calls to the client.
    ctx := opentracing.ContextWithSpan(context.Background(), span)
    //ctx := context.Background()
    getReq:=&pb.GetReq{Key:key}
    getResp, err :=c.Get(ctx, getReq )
    value := getResp.Value
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇golang面对对象 下一篇GoLang中面向对象的三大特性

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目