当前位置:首页 > 前沿科技 > 正文

基于 OpenTelemetry 的链路追踪

链路追踪的前世今生起源链路追踪(DistributedTracing)一词最早出现于谷歌发布的论文《Dapper,aLarge-ScaleDistributedSystemsTracingInfrastructure》中,这篇论文对于实现链路追踪,对于后来出现的Jaeger、Zipkin等开源分布式...

链路追踪的前世今生起源链路追踪(DistributedTracing)一词最早出现于谷歌发布的论文《Dapper,aLarge-ScaleDistributedSystemsTracingInfras......

链路追踪的前世今生起源

链路追踪(DistributedTracing)一词最早出现于谷歌发布的论文《Dapper,aLarge-ScaleDistributedSystemsTracingInfrastructure》中,这篇论文对于实现链路追踪,对于后来出现的Jaeger、Zipkin等开源分布式追踪项目设计理念仍有很深的影响。

微服务架构是一个分布式的架构,会有很多个不同的服务。不同的服务之前相互调用,如果出现了错误由于一个请求经过了N个服务。随着业务的增加越来越多的服务之间的调用,如果没有一个工具去记录调用链,解决问题的时候就会像下面图片里小猫咪玩的毛线球一样,毫无头绪,无从下手。

所以需要有一个工具能够清楚的了解一个请求经过了哪些服务,顺序是如何,从而能够轻易的定位问题。


百家争艳

从谷歌发布Dapper后,分布式链路追踪工具越来越多,以下简单列举了一些常用的链路追踪系统

Skywalking

阿里鹰眼

大众点评CAT

TwitterZipkin

Naverpinpoint

UberJaeger

争锋相对?

随着链路追踪工具越来越多,开源领域主要分为两派,一派是以CNCF技术委员会为主的OpenTracing的规范,例如jaegerzipkin都是遵循了OpenTracing的规范。而另一派则是谷歌作为发起者的OpenCensus,而且谷歌本身还是最早提出链路追踪概念的公司,后期连微软也加入了OpenCensus


OpenTelemetry诞生

微软加入OpenCensus后,直接打破了之前平衡的局面,间接的导致了OpenTelemetry的诞生谷歌和微软下定决心结束江湖之乱,首要的问题是如何整合两个两个社区已有的项目,OpenTelemetry主要的理念就是,兼容OpenCensus和OpenTracing,可以让使用者无需改动或者很小的改动就可以接入OpenTelemetry

Kratos的链路追踪实践tracing中间件

kratos框架提供的自带中间件中有一个名为tracing中间件,它基于Opentelemetry实现了kratos框架的链路追踪功能,中间件的代码可以从middleware/tracing中看到。

实现原理

kratos的链路追踪中间件由三个文件组成,,。client和server的实现原理基本相同,本文以server实现进行原理解析。

首先当请求进入时,tracing中间件会被调用,首先调用了中的NewTracer方法

//(optsOption){//调用中的NewTracer传入了一个SpanKindServer和配置项tracer:=NewTracer(,opts)//省略代码}

中的NewTracer方法被调用后会返回一个Tracer,实现如下

funcNewTracer(,optsOption)*Tracer{options:=options{}for_,o:=rangeopts{o(options)}//判断是否存在otel追踪提供者配置,如果存在则设置!=nil{()}/*判断是否存在Propagators设置,如果存在设置则覆盖,不存在则设置一个默认的TextMapPropagator注意如果没有设置默认的TextMapPropagator,链路信息则无法正确的传递*/!=nil{()}else{(({},{}))}varnamestring//判断当前中间件的类型,是server还是clientifkind=={name="server"}elseifkind=={name="client"}else{panic(("unsupportedspankind:%v",kind))}//调用otel包的Tracer方法传入name用来创建一个tracer实例tracer:=(name)returnTracer{tracer:tracer,kind:kind}}

判断当前请求类型,处理需要采集的数据,并调用中的Start方法

var()//判断请求类型ifinfo,ok:=(ctx);ok{//HTTPcomponent="HTTP"//取出请求的地址operation=//调用otel/propagation包中的HeaderCarrier,会处理以用来满足TextMapCarrierinterface//TextMapCarrier是一个文本映射载体,用于承载信息carrier=()//().Extract()方法用于将文本映射载体,读取到上下文中ctx=().Extract(ctx,())}elseifinfo,ok:=(ctx);ok{//Grpccomponent="gRPC"operation=////调用grpc/metadata包中(ctx)传入ctx,转换grpc的元数据ifmd,ok:=(ctx);ok{//调用中的MetadataCarrier将MD转换成文本映射载体carrier=MetadataCarrier(md)}}//调用方法ctx,span:=(ctx,component,operation,carrier)//省略代码}

调用中的Start方法

func(t*Tracer)Start(,componentstring,operationstring,)(,){//判断当前中间件如果是server则将carrier注入到上下文中=={ctx=().Extract(ctx,carrier)}//调用otel/tracer包中的start方法,用来创建一个spanctx,span:=(ctx,//中声明的请求路由作为spanNameoperation,//设置span的属性,设置了一个component,component的值为请求类型(("component",component)),//设置span种类(),)//判断如果当前中间件是client则将carrier注入到请求里面=={().Inject(ctx,carrier)}returnctx,span}

defer声明了一个闭包方法

//这个地方要注意,需要使用闭包,因为defer的参数是实时计算的如果异常发生,err会一直为nil//(){(ctx,span,err)}()

中间件继续执行

//行reply,err=handler(ctx,req)

中间件调用结束defer中的闭包被调用后执行了中的方法

func(t*Tracer)(,,errerror){//判断是否有异常发生,如果有则设置一些异常信息iferr!=nil{//记录异常(err)//设置span属性(//设置事件为异常("event","error"),//设置message为().("message",()),)//设置了span的状态(,())}else{//如果没有发生异常,span状态则为(,"OK")}//中止()}
如何使用

tracing中间件的使用示例可以从kratos/examples/traces,该示例简单的实现了跨服务间的链路追踪,以下代码片段包含部分示例代码。

//,得到一个TracerProviderfunctracerProvider(urlstring)(*,error){//examples/traces中使用的是jaeger,其他方式可以查看opentelemetry官方示例exp,err:=(((url)))iferr!=nil{returnnil,err}tp:=((()),//设置Batcher,注册jaeger导出程序(exp),//记录一些默认信息(((_),("environment","development"),("ID",1),)),)returntp,nil}
在grpc/server中使用
//((":9000"),(//((tp),),),)
在grpc/client中使用
//(ctx,("127.0.0.1:9000"),(((),(({},{}),),)),(2*),)
在http/server中使用
//((":8000"))("/",(s,(//((tp),(({},{}),),),),)
在http/client中使用
(ctx,(((),),))
如何实现一个其他场景的tracing

我们可以借鉴kratos的tracing中间件的代码来实现例如数据库的tracing,如下面的代码片段,作者借鉴了tracing中间件,实现了qmgo库操作MongoDB数据库的tracing。

funcmongoTracer(,,commandinterface{}){var()(tp)reply={}switchvalue:=command.(type){case*:commandName===="CommandStartedEvent"case*:commandName===="CommandSucceededEvent"case*:commandName====="CommandFailedEvent"}duration,_:=((nanos,10)+"ns")tracer:=("mongodb")kind:=,span:=(ctx,commandName,(("event",eventName),("command",commandName),("query",()),("queryId",queryId),("ms",()),),(kind),)iffailure!=""{((failure))}()}
参考文献

谷歌论文《Dapper,aLarge-ScaleDistributedSystemsTracingInfrastructure》-

OpenTelemetry官网-

KubeCon2019OpenTelemetry分享-

Kratos框架-

traces示例-

最新文章