链路追踪建设分享
本文为笔者在去年工作中实践后的总结分享

背景
•开发跨部门权限问题,无法在线上(测试、预发布、正式环境)查看容器内错误日志•微服务化,即使有权限查看容器日志,业务出现bug时排查定位问题所在的微服务很慢很麻烦•被动发现问题。比如说某些api的请求出现异常,被动等待下游反馈
链路追踪如何解决问题
•上报每一个请求和响应的详细数据,包括header头,路由,请求参数,请求体,通信状态,以及支持自定义更多参数上报。•阿里采集,提供查询接口,开发可以另外搭建查询服务独立查询,无需权限•采用OpenTracing标准,分布式追踪,可以跨语言跨服务形成链路调用日志,一个请求经过了哪些服务,在哪里发生了故障被截断一目了然•通过请求和响应状态可以为链路日志打上状态标签,然后上报到Prometheus统计指标。可以另外扩展出指标异常统计告警机制,及时主动获知错误发生,在下游反馈前解决问题
原理简述

Jaeger参考文档[1]
•header头注入标识,支持多种标识(b3
,uber-trace-id
等)。我们采用jaeger默认的uber-trace-id
标记链路id。•id值格式为:{trace-id}:{span-id}:{parent-span-id}:{flags}
。•trace-id:链路追踪id,一次请求从头到尾的唯一标识•span-id:span的标识。span是链路追踪的基本单位,表示一个独立的工作单元,可以是一次函数调用,对一个服务的请求等。记录了服务名称,开始结束时间,以及服务打上的tag(上文说的状态异常标签,error=true
),logs(记录请求和响应的详细数据)•parent-span-id:父span标识•flags:采样标志•作为服务端,接收请求的时候检测是否有uber-trace-id
标识,如果没有就创建一个,如果有则获取值并按照格式拆分,然后把请求中的span-id
改成自身的parent-span-id
,生成一个新的span-id
•作为客户端,对其他服务发起调用,在请求头注入uber-trace-id
标识,传递给被调用服务•服务在一次请求和响应完成后独自上报span•一个trace由多个span组成,表示了一次完整的链路追踪,按照span的父子关系向下排列,以时间轴的方式展示。
落地实践

•原子服务集成上报中间件•部署Jaeger服务端,包括Jaeger Agent和Jaegger Collector•Agent采用udp协议与客户端(原子服务)通信,无状态,消耗低,适用每个请求都上报的场景。客户端发送就完事,不关心Agent是否收到•Agent收集一定量链路日志后批量上报给Collector,用的是http协议,然后Collector再写入到数据库里•阿里提供了日志数据库存储服务。链路日志存储到第三方•通过另外部署的Jaeger Query客户端读取阿里存储的日志,完全和生产服务解耦。可以用默认的客户端查询,或者将客户端暴露的api集成到Grafana查阅。•go服务端采用的第三方上报工具内置了指标暴露接口,可以被promehteus采集统计请求状态
GIN中间件
gin的中间件需要自己实现:
代码库地址:yuchanns/bullets[2]
go get -u github.com/yuchanns/bullets
•使用中间件
package mainimport ("context""github.com/gin-gonic/gin""github.com/yuchanns/bullets/common""github.com/yuchanns/bullets/common/middlewares""os")func main() {g := gin.Default()// 服务名serviceName := "openapi-service"// 上报agent地址agentAddr := os.Getenv("OPENTRACING_AGENT")// 操作前缀operationPrefix := []byte("api-request-")opentracerCloseFunc, opentracerMiddleware, err := middlewares.BuildOpenTracerInterceptor(serviceName, agentAddr, operationPrefix)if err != nil {common.Logger.Error(context.Background(), err)} else {defer opentracerCloseFunc()g.Use(opentracerMiddleware)}}
•自定义打tag
import ("github.com/gin-gonic/gin""github.com/opentracing/opentracing-go""github.com/opentracing/opentracing-go/log""github.com/pkg/errors")func CustomTag(ctx *gin.Context) {if cspan, ok := ctx.Get("tracing-context"); ok {if span, ok := cspan.(opentracing.Span); ok {span.SetTag("error", true)span.LogFields(log.Error(errors.New("err")))span.LogFields(log.String("exampleKey", "stringValue"))}}}
GRPC中间件
代码库地址:yuchanns/gobyexample/grpc-app[3]
grpc生态系统[4]自带中间件,使用起来较为方便:
package mainimport ("io""log""os"grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery""github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc""github.com/opentracing/opentracing-go""github.com/uber/jaeger-client-go""google.golang.org/grpc")func NewJaegerTracer(name, hostPort string) (opentracing.Tracer, io.Closer, error) {tp, err := jaeger.NewUDPTransport(hostPort, 0)if err != nil {return nil, nil, err}reporter := jaeger.NewRemoteReporter(tp)var sampler jaeger.Samplersampler = jaeger.NewConstSampler(true)tracer, closer := jaeger.NewTracer(name,sampler,reporter,)opentracing.SetGlobalTracer(tracer)return tracer, closer, nil}func main() {tracer, closer, err := NewJaegerTracer("testApp", os.Getenv("AGENT_HOST_PORT"))if err != nil {log.Fatalf("failed to create Jaeger tracer")}defer closer.Close()opts := []grpc.ServerOption{grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(otgrpc.OpenTracingStreamServerInterceptor(tracer, otgrpc.LogPayloads()),grpc_recovery.StreamServerInterceptor(),)),grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(otgrpc.OpenTracingServerInterceptor(tracer, otgrpc.LogPayloads()),grpc_recovery.UnaryServerInterceptor(),)),}srv := grpc.NewServer(opts...)// ...register your grpc serverif err := srv.Serve(s.l); err != nil {log.Fatalf("failed to serve: %v", err)}}
引用链接
[1]
Jaeger参考文档: https://www.jaegertracing.io/docs/1.11/client-libraries/[2]
yuchanns/bullets: https://github.com/yuchanns/bullets[3]
yuchanns/gobyexample/grpc-app: https://github.com/yuchanns/gobyexample/tree/monorepo/grpc-app[4]
grpc生态系统: https://github.com/grpc-ecosystem




