跟踪器|Go微服务全链路跟踪详解

本文作者:倚天码农 , 授权发布
在微服务架构中 , 调用链是漫长而复杂的 , 要了解其中的每个环节及其性能 , 你需要全链路跟踪 。 它的原理很简单 , 你可以在每个请求开始时生成一个唯一的ID , 并将其传递到整个调用链 。 该ID称为CorrelationID[1]1 , 你可以用它来跟踪整个请求并获得各个调用环节的性能指标 。 简单来说有两个问题需要解决 。 第一 , 如何在应用程序内部传递ID; 第二 , 当你需要调用另一个微服务时 , 如何通过网络传递ID 。
什么是OpenTracing? 现在有许多开源的分布式跟踪库可供选择 , 其中最受欢迎的库可能是Zipkin[2]2和Jaeger[3]3 。 选择哪个是一个令人头疼的问题 , 因为你现在可以选择最受欢迎的一个 , 但是如果以后有一个更好的出现呢?OpenTracing[4]?可以帮你解决这个问题 。 它建立了一套跟踪库的通用接口 , 这样你的程序只需要调用这些接口而不被具体的跟踪库绑定 , 将来可以切换到不同的跟踪库而无需更改代码 。 Zipkin和Jaeger都支持OpenTracing 。
如何跟踪服务器端点(server endpoints)?
在下面的程序中我使用“Zipkin”作为跟踪库 , 用“OpenTracing”作为通用跟踪接口 。 跟踪系统中通常有四个组件 , 下面我用Zipkin作为示例:

  • recorder(记录器):记录跟踪数据
  • Reporter (or collecting agent)(报告器或收集代理):从记录器收集数据并将数据发送到UI程序
  • Tracer:生成跟踪数据
  • UI:负责在图形UI中显示跟踪数据

跟踪器|Go微服务全链路跟踪详解文章插图
上面是Zipkin的组件图 , 你可以在Zipkin Architecture[5]中找到它 。
有两种不同类型的跟踪 , 一种是进程内跟踪(in-process) , 另一种是跨进程跟踪(cross-process) 。 我们将首先讨论跨进程跟踪 。
客户端程序:
我们将用一个简单的gRPC程序作为示例 , 它分成客户端和服务器端代码 。 我们想跟踪一个完整的服务请求 , 它从客户端到服务端并从服务端返回 。 以下是在客户端创建新跟踪器的代码 。 它首先创建“HTTP Collector”(the agent)用来收集跟踪数据并将其发送到“Zipkin” UI ,“endpointUrl”是“Zipkin” UI的URL 。 其次 , 它创建了一个记录器(recorder)来记录端点上的信息 , “hostUrl”是gRPC(客户端)呼叫的URL 。 第三 , 它用我们新建的记录器创建了一个新的跟踪器(tracer) 。 最后 , 它为“OpenTracing”设置了“GlobalTracer” , 这样你可以在程序中的任何地方访问它 。
跟踪器|Go微服务全链路跟踪详解文章插图
以下是gRPC客户端代码 。 它首先调用上面提到的函数“newTrace()”来创建跟踪器 , 然后 , 它创建一个包含跟踪器的gRPC调用连接 。 接下来 , 它使用新建的gRPC连接创建缓存服务(Cache service)的gRPC客户端 。 最后 , 它通过gRPC客户端来调用缓存服务的“Get”函数 。
跟踪器|Go微服务全链路跟踪详解文章插图
Trace 和 Span:在OpenTracing中 , 一个重要的概念是“trace” , 它表示从头到尾的一个请求的调用链 , 它的标识符是“traceID” 。 一个“trace”包含有许多跨度(span) , 每个跨度捕获调用链内的一个工作单元 , 并由“spanId”标识 。 每个跨度具有一个父跨度 , 并且一个“trace”的所有跨度形成有向无环图(DAG) 。 以下是跨度之间的关系图 。 你可以从The OpenTracing Semantic Specification[6]中找到它 。
跟踪器|Go微服务全链路跟踪详解文章插图
以下是函数“callGet”的代码 , 它调用了gRPC服务端的“Get"函数 。 在函数的开头 , OpenTracing为这个函数调用开启了一个新的span , 整个函数结束后 , 它也结束了这个span 。
跟踪器|Go微服务全链路跟踪详解文章插图
服务端代码:
下面是服务端代码 , 它与客户端代码类似 , 它调用了“newTracer()”(与客户端“newTracer()”函数几乎相同)来创建跟踪器 。 然后 , 它创建了一个“OpenTracingServerInterceptor” , 其中包含跟踪器 。 最后 , 它使用我们刚创建的拦截器(Interceptor)创建了gRPC服务器 。
跟踪器|Go微服务全链路跟踪详解文章插图
以下是运行上述代码后在Zipkin中看到的跟踪和跨度的图片 。 在服务器端 , 我们不需要在函数内部编写任何代码来生成span , 我们需要做的就是创建跟踪器(tracer) , 服务器拦截器自动为我们生成span 。